# qURL™ Service API

- **OpenAPI Version:** `3.1.1`
- **API Version:** `1.0.0`

Use the qURL API to create temporary access links to protected resources.

## Start here

A **CRID** (`crid`) is the permanent identity of a protected **resource**. A **qURL** is an access link to that resource, with its own expiration and access rules. You can create multiple qURLs for the same CRID and revoke each link separately.

| Value                                                                 | Use it to                                                                                                                                        |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `crid`                                                                | Identify and manage the resource. It stays the same throughout the resource's life and is never reused after deletion. It does not grant access. |
| `qurl_id`                                                             | Update or revoke one qURL without changing the other links.                                                                                      |
| `qurl_link` in create or mint responses; `qurl` in `/share` responses | The secret access link to share with a recipient.                                                                                                |

Treat access links as secrets. Use them as returned. Do not construct them or extract resource IDs from them.

## Quickstart: register, share, and revoke

Use **Resources** for new integrations. The steps below register a URL, create a one-hour qURL, and revoke that link. They use Bash, curl 7.76 or later, jq, and uuidgen. Set `QURL_API_KEY` to an account API key with `qurl:read` and `qurl:write`. Set `TARGET_URL` to the HTTPS URL you want to protect. Run the steps in the same shell.

**1. Register the resource and save its CRID.**

```bash
set -euo pipefail
: "${QURL_API_KEY:?Set QURL_API_KEY to your account API key}"
: "${TARGET_URL:?Set TARGET_URL to your HTTPS target URL}"
QURL_API_URL="${QURL_API_URL:-https://api.layerv.ai}"
RESOURCE_URL="$QURL_API_URL/v1/resources"

resource=$(curl --fail-with-body --silent --show-error \
  "$RESOURCE_URL" \
  -H "Authorization: Bearer $QURL_API_KEY" \
  -H 'Content-Type: application/json' \
  --data "$(jq -n --arg url "$TARGET_URL" '{type: "url", target_url: $url}')")
CRID=$(jq -er '.data.crid' <<< "$resource")
```

Expect `201`. Repeating this request for the same URL and account returns the existing active resource. Registration alone does not create a qURL access link.

**2. Create a link for a recipient.**

```bash
MINT_KEY=$(uuidgen)
link=$(curl --fail-with-body --silent --show-error \
  "$RESOURCE_URL/$CRID/qurls" \
  -H "Authorization: Bearer $QURL_API_KEY" \
  -H "Idempotency-Key: $MINT_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"expires_in":"1h","one_time_use":false,"max_sessions":1,"session_duration":"15m","label":"Quickstart recipient"}')
QURL_ID=$(jq -er '.data.qurl_id' <<< "$link")
ACCESS_LINK=$(jq -er '.data.qurl_link' <<< "$link")
printf '%s\n' "$ACCESS_LINK"
```

Expect `201`. Open the printed link in a browser, or share it with the intended recipient. The link permits one concurrent session, with a 15-minute session lifetime. The same link can start another session while it is still valid. Keep the printed value out of logs and source control. If the request times out, retry the `link=$(curl ...)` command with the same `MINT_KEY`; do not run `uuidgen` again for that retry.

**3. Revoke this link when access is no longer needed.**

```bash
curl --fail-with-body --silent --show-error \
  -X DELETE "$RESOURCE_URL/$CRID/qurls/$QURL_ID" \
  -H "Authorization: Bearer $QURL_API_KEY"
```

Expect `204` with no body. The resource remains available for new qURLs. If revocation returns `503`, wait for `Retry-After` and repeat the same DELETE. Session timing and already-open connections are described in `CreateQurlRequest.session_duration` under **Timing precision**.

## Choose an operation

| Task                                   | Recommended operation                                  |
| -------------------------------------- | ------------------------------------------------------ |
| Register a resource without a link     | `POST /v1/resources`                                   |
| Create a link with chosen access rules | `POST /v1/resources/{id}/qurls`                        |
| List or get resources                  | `GET /v1/resources` or `GET /v1/resources/{id}`        |
| List all links for one resource        | `GET /v1/resources/{id}/qurls`                         |
| Update or revoke one link              | `PATCH` or `DELETE /v1/resources/{id}/qurls/{qurl_id}` |
| Revoke a resource and all its links    | `DELETE /v1/resources/{id}`                            |

Put the CRID in `{id}`. Use these alternatives only when they fit your task:

| Alternative                     | When to use it                                                                                                                            |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `POST /v1/qurls`                | Create a resource and its first link from a target URL in one request.                                                                    |
| `POST /v1/resources/{id}/share` | Create a link with resource defaults using `qurl:resolve`. It accepts a lifetime, not custom access rules, and does not guarantee replay. |
| `POST /v1/qurls/{id}/mint_link` | Maintain an existing integration that uses this route.                                                                                    |

The `/v1/qurls` list, get, update, and delete operations act on resources despite their names. Use the Resources operations above to keep resource and link actions clear. Older resources can omit `crid`; use their returned `resource_id` public key as a fallback.

## Authentication

Send your API key or Auth0 access token in the header:

```http
Authorization: Bearer YOUR_API_KEY_OR_ACCESS_TOKEN
```

Use an API key for scripts and services. Use an Auth0 access token for operations that require JWT authentication. Do not send credentials in URLs or request bodies. Each operation lists its required permissions and any credential restrictions.

| Credential               | Where to use it                                                |
| ------------------------ | -------------------------------------------------------------- |
| API key                  | Operations that accept API keys, within the key's scopes       |
| Auth0 access token (JWT) | Interactive account management and operations that accept JWTs |
| Enrollment token         | Enroll one agent or Connector; not a general API credential    |
| Device credential        | The enrolled device's permitted Connector operations           |

Scopes:

- `qurl:read`: Read resources, qURLs, and quota.
- `qurl:write`: Create, update, and revoke resources and qURLs; manage webhooks.
- `qurl:resolve`: Redeem access tokens without a browser and share resources by CRID.
- `qurl:agent`: Manage agent and qURL Connector enrollment and assignments.
- `qurl:keys`: Manage credentials with explicit automation authority. See the API Keys operations for restrictions.

## Retries and idempotency

Check the operation’s retry rules before retrying a write. Where replay is supported, choose a new `Idempotency-Key` for each new operation. After a timeout, retry with the same key, method, path, and body. Do not reuse a key for a different request. The standard replay window is 24 hours; operations with different rules describe them separately. A replay can return a previously issued secret. Store successful responses securely. Resource sharing does not guarantee replay, even if you send `Idempotency-Key`. API-key creation, external identity bindings, billing checkout, and delegated batches have their own replay rules. Read their header and response descriptions.

## Responses and errors

Most successful JSON responses contain `data` and `meta`:

```json
{
  "data": [],
  "meta": {
    "request_id": "req_abc123",
    "page_size": 0,
    "has_more": false,
    "next_cursor": ""
  }
}
```

List responses include pagination fields. Send `meta.next_cursor` as `cursor` to get the next page. Continue while `meta.has_more` is `true`, even if a page contains fewer items than requested. Responses with status `204` or `304` have no body. Examples focus on the fields needed for the task. Schemas list all available fields. Some operations use a different JSON shape; follow the operation's response schema.

Errors contain an `error` object with Problem Details fields and a stable `code`:

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": { "request_id": "req_abc123" }
}
```

Use `error.code` for program decisions and `error.detail` to explain the failure. Validation errors can include `error.invalid_fields`, a map of field names to errors. Include `meta.request_id` when you contact support.

A `4xx` response can carry the fixed `error.detail` `The request was refused. The reason cannot be shown.` in place of a specific reason. The API sends it when the specific reason contains text that it does not disclose. The request was still refused for the reason that the status and `error.code` name. A failed item of a batch response can carry the same text as its `error.message`; the item's `error.code` still applies. If the status and the code are not enough to correct the request, send `meta.request_id` to support.

## Rate limits and caching

Request limits apply per account and vary by plan. On `429`, wait for the number of seconds in `Retry-After` before retrying. For GET operations that return an `ETag`, send it in `If-None-Match` on the next request. A `304` means you can reuse the previous response.

## Webhooks

Use the Webhooks operations to subscribe to events. Verify the `QURL-Signature` header with your webhook secret before processing a payload. See `WebhookPayload` for event availability, payload fields, and privacy rules.

## Servers

- **URL:** `https://api.layerv.ai`
  - **Description:** Production

## Operations

### Get browser sign-in settings

- **Method:** `GET`
- **Path:** `/v1/account/auth`
- **Tags:** Account

Returns public sign-in settings for optional account setup.

#### Responses

##### Status: 200 Browser sign-in settings

###### Content-Type: application/json

- **`audience` (required)**

  `string`

- **`client_id` (required)**

  `string`

- **`domain` (required)**

  `string`

**Example:**

```json
{
  "domain": "",
  "client_id": "",
  "audience": ""
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: default Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Link this device to an account

- **Method:** `POST`
- **Path:** `/v1/account/link`
- **Tags:** Account

Requires this device's credential and a verified account access token. Existing resource IDs and links stay unchanged. A device can be linked to only one account.

#### Request Body

##### Content-Type: application/json

- **`account_token` (required)**

  `string`

**Example:**

```json
{
  "account_token": ""
}
```

#### Responses

##### Status: 200 Device linked

###### Content-Type: application/json

- **`account_id` (required)**

  `string`

- **`owner_id` (required)**

  `string`

**Example:**

```json
{
  "owner_id": "",
  "account_id": ""
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: default Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### List account resource owners

- **Method:** `GET`
- **Path:** `/v1/account/owners`
- **Tags:** Account

Lists the account and its linked devices. Account access tokens can select a linked owner with the X-QURL-Owner header on management requests.

#### Responses

##### Status: 200 Account resource owners

###### Content-Type: application/json

- **`owners` (required)**

  `array`

  **Items:**

  `string`

**Example:**

```json
{
  "owners": [
    ""
  ]
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: default Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Redeem an access token without a browser

- **Method:** `POST`
- **Path:** `/v1/resolve`
- **Tags:** Resolution

Redeems an access token and grants network access to the IP address that makes this request. Use this operation for scripts and agents that do not use a browser. Access the returned `target_url` from the same IP before the grant expires. The response's `src_ip` shows which IP received access.

If access setup fails after a one-time token is consumed, no access is granted and the token cannot be used again. Obtain a new link. Multi-use tokens can be retried.

#### Request Body

##### Content-Type: application/json

- **`access_token` (required)**

  `string` — qURL access token (e.g., "at\_k8xqp9h2sj9lx7r4abcdef")

**Example:**

```json
{
  "access_token": "at_k8xqp9h2sj9lx7r4abcdef"
}
```

#### Responses

##### Status: 200 Token resolved and network access granted

###### Content-Type: application/json

- **`data`**

  `object`

  - **`access_grant`**

    `object` — Details of the network access that was granted

    - **`expires_in` (required)**

      `integer` — Seconds until network access expires

    - **`granted_at` (required)**

      `string`, format: `date-time` — When the access was granted

    - **`src_ip` (required)**

      `string` — The IP address that was granted access

  - **`resource_id`**

    `string` — Resource public key. This response does not include a CRID.

  - **`target_url`**

    `string`, format: `uri` — The URL that is now accessible from the caller's IP

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "target_url": "https://api.example.com/data",
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "access_grant": {
      "expires_in": 305,
      "granted_at": "2026-03-09T15:30:00Z",
      "src_ip": "203.0.113.42"
    }
  },
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 410 The resource or access token is no longer available; check error.code

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 502 Network access could not be granted

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/qurls

- **Method:** `PARAMETERS`
- **Path:** `/v1/qurls`

### Create an access link from a URL

- **Method:** `POST`
- **Path:** `/v1/qurls`
- **Tags:** QURLs

Creates an access link for a target URL. Creates the resource if you do not already have one for that URL. Save `crid` to manage the resource and `qurl_id` to manage this link. Share `qurl_link` with the recipient.

If your resource for that URL has passed its expiration, the request returns `400 invalid_input`. Set a later expiration with `PATCH /v1/qurls/{id}`, or revoke that resource and send the request again.

A `503` means that no link was created and that the request can be sent again after the time in `Retry-After`. One cause is that your resource for that URL was closed while the request was running: the retry creates a new resource. A `503` response is not stored for replay, so the retry runs again even with the same `Idempotency-Key`.

A resource that this request creates is private unless the request sends `private: false`. Resource privacy cannot change after creation. If you already have a resource for that URL, the request reuses it with the privacy it has. A request that states `private` with a different value than that resource returns `400 privacy_mismatch`: omit `private` to reuse the resource. To list the devices that may create links for a private resource, use `PATCH /v1/resources/{id}`.

Start with `target_url` and `expires_in`. Add an access policy only when needed. For a qURL Connector, `403 connector_disabled` means Connector operations are unavailable. Use `error.code` to distinguish this from a permission error.

#### Request Body

##### Content-Type: application/json

- **`access_policy`**

  `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

  - **`ai_agent_policy`**

    `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

    - **`allow_categories`**

      `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`block_all`**

      `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

    - **`deny_categories`**

      `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

  - **`geo_allowlist`**

    `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`geo_denylist`**

    `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`ip_allowlist`**

    `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`ip_denylist`**

    `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`user_agent_allow_regex`**

    `string` — Regular expression to allow matching user agents (max 256 characters)

  - **`user_agent_deny_regex`**

    `string` — Regular expression to deny matching user agents (max 256 characters)

- **`custom_domain`**

  `string` — Optional custom domain to assign to the auto-created resource. The domain must be registered, active, and owned by the caller. Only allowed when the target URL creates a new resource — rejected if a resource already exists for this target URL.

- **`expires_in`**

  `string` — How long the new link remains valid. Examples: \`5m\`, \`1h\`, \`7d\`, \`1w\`. Minimum: 1 minute. Maximum: 30 days on all plans. The resource's expiration can shorten the link's lifetime; read \`expires\_at\` in the response. When omitted or empty, qURL Connector links use 5 minutes. URL and integration-owned upload links use the service default. Set this field explicitly for a predictable lifetime.

- **`label`**

  `string` — Human-readable label identifying who this qURL is for

- **`max_sessions`**

  `integer` — Maximum concurrent sessions for this qURL, not the whole resource. Use \`0\` for no limit or \`1\`–\`1000\` for a limit. Set \`one\_time\_use: false\` explicitly when you need multiple visitors: some resource types otherwise create a single-visitor link even when \`max\_sessions\` is \`0\`. Visitors that share a source IP count as one session and share its IP policy checks.

- **`one_time_use`**

  `boolean` — Whether the link is consumed by its first successful use. Explicit \`true\` or \`false\` takes precedence over the resource defaults. When omitted, URL and integration-owned upload links use \`false\`. qURL Connector links use \`true\` unless \`max\_sessions\` is greater than 1. For a reusable Connector link, explicitly send \`one\_time\_use: false\`, including when \`max\_sessions\` is \`0\` (unlimited).

- **`private`**

  `boolean` — Privacy of the resource, used only when this request creates it. When omitted, a new resource is private. Send \`false\` to create a public resource. Privacy cannot change after creation. When the request reuses your existing resource for the same \`target\_url\`, omit this field: the resource keeps the privacy it has. A value that differs from the existing resource returns \`400 privacy\_mismatch\`.

- **`session_duration`**

  `string` — How long a session permits new requests after the recipient first reaches the content. This is separate from \`expires\_in\`, which controls the link's expiration. \*\*Default and limits:\*\* Supports seconds (\`30s\`), minutes (\`5m\`), hours (\`1h\`), and days (\`1d\`). Minimum: 1 second. Maximum: 24 hours. If omitted, uses the resource cap when set. Without a cap, qURL Connectors use 1 hour; other resource types use the service default. Set this field explicitly when timing matters. \*\*Resource cap:\*\* On the first create for a target URL, an explicit value also sets the resource's fixed \`session\_duration\_cap\`. Later creates, mints, and updates cannot exceed it (\`400 invalid\_session\_duration\`). See \`ResourceData.session\_duration\_cap\` for how to create a resource with a different cap. \*\*Timing precision:\*\* For signed browser qURLs with a configured duration, the first content request must be authorized within 10 seconds of the start of admission, including time spent completing admission. The session timer then runs for \`session\_duration\` from that first authorization. Initial network admission lasts \`session\_duration + 10 seconds\`; it does not double for long sessions. A 24-hour session gets 24 hours plus 10 seconds of initial network admission. Opening the original invitation again is not required during a live session, including after a TCP connection becomes idle. When duration is omitted on an uncapped URL resource, the existing default authorization deadline is 24 hours plus 10 seconds from admission; the configured-duration first-arrival rule does not apply. New requests are denied at session expiry. Active requests, streams, and WebSockets are canceled at that deadline. The network allowance does not extend application authorization. A single-use invitation remains consumed if the first content request misses the 10-second arrival deadline. Cancellation cannot retract data already delivered or buffered by the client.

- **`target_url`**

  `string`, format: `uri` — The URL to protect with qURL (max 2048 characters). Required for standard reverse-proxy qURLs.

- **`type`**

  `string`, default: `"url"` — Type of resource the qURL grants access to. Defaults to \`url\`; other values are restricted to specific integrations. See \`ResourceType\`.

**Example:**

```json
{
  "type": "url",
  "private": true,
  "target_url": "https://dashboard.example.com",
  "expires_in": "1h",
  "one_time_use": true,
  "max_sessions": 0,
  "session_duration": "1h",
  "access_policy": {
    "ip_allowlist": [
      "192.168.1.0/24",
      "10.0.0.1"
    ],
    "ip_denylist": [
      ""
    ],
    "geo_allowlist": [
      "US",
      "CA",
      "GB"
    ],
    "geo_denylist": [
      "CN",
      "RU"
    ],
    "user_agent_allow_regex": "^Mozilla.*",
    "user_agent_deny_regex": ".*bot.*",
    "ai_agent_policy": {
      "block_all": false,
      "deny_categories": [
        "gptbot",
        "commoncrawl",
        "bytedance"
      ],
      "allow_categories": [
        "claude",
        "chatgpt",
        "perplexity"
      ]
    }
  },
  "label": "Alice from Acme",
  "custom_domain": "app.example.com"
}
```

#### Responses

##### Status: 201 qURL created successfully

###### Content-Type: application/json

- **`data`**

  `object` — Response data for qURL creation

  - **`branded_domain`**

    `string` — Custom hostname to display as link text. Keep \`qurl\_link\` as the destination. See \`MintLinkData.branded\_domain\` for domain status requirements. An idempotent retry returns the original value. Check the resource and domain again if you need the current domain status.

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`expires_at`**

    `string`, format: `date-time`

  - **`label`**

    `string` — Human-readable label for this qURL

  - **`qurl_id`**

    `string` — Identifier for this qURL. Save it to revoke or update this link, or match its webhook events.

  - **`qurl_link`**

    `string`, format: `uri` — Access link to share with the recipient. Store it securely; later GET requests do not return it. Exact request retries can replay the creation response within the replay window.

  - **`qurl_site`**

    `string`, format: `uri` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

  - **`target_path`**

    `string` — Redirect destination and authorization scope for this new qURL on a qURL Connector resource. Omitted when the new qURL uses the bare origin. Each create call makes an independent qURL; omission affects only that qURL.

  - **`type`**

    `string` — Resource type — echoes the type from the create request.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "qurl_id": "q_3a7f2c8e91b",
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "qurl_link": "",
    "branded_domain": "",
    "qurl_site": "",
    "expires_at": "",
    "target_path": "",
    "label": "",
    "type": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Forbidden while creating a qURL; inspect error.code

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 An active resource already protects this URL with a different type (\`resource\_type\_conflict\`). Use its CRID with \`POST /v1/resources/{id}/qurls\`, or use another target URL. If you revoke and replace the existing resource, its old links stop working. After resolving the conflict, use a new \`Idempotency-Key\`. The original key can replay this \`409\` response for 24 hours.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### List resources

- **Method:** `GET`
- **Path:** `/v1/qurls`
- **Tags:** QURLs

Lists your resources, each of which can have multiple qURLs. Use `GET /v1/resources/{id}/qurls` with a resource's CRID to list its individual links. Supports status and date filters and cursor-based pagination.

#### Responses

##### Status: 200 List of resources

###### Content-Type: application/json

- **`data`**

  `array`

  **Items:**

  - **`created_at`**

    `string`, format: `date-time` — When the resource was created.

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`custom_domain`**

    `string | null` — Custom domain associated with this resource

  - **`description`**

    `string` — Human-readable description

  - **`expires_at`**

    `string`, format: `date-time` — When the resource expires. Each qURL can have its own earlier expiration.

  - **`qurl_count`**

    `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

  - **`qurl_site`**

    `string`, format: `uri` — Routing URL for the resource. It does not grant access by itself. Share a qURL access link with the recipient. Do not extract an identifier from this hostname.

  - **`qurls`**

    `array` — Unordered bounded preview of per-qURL details (maximum 100). Best-effort on single-get and omitted on list, preview lookup failure, or redacted resource types. This preview does not imply newest first or any stable ordering and is not active-first; an active qURL can be absent from the preview when earlier qURLs fill the cap. When present, \`qurl\_count\` is the exact total; if omitted, callers must treat truncation as unknown. For the complete per-resource qURL list, use \`GET /v1/resources/{id}/qurls\`, which pages through every qURL with cursor-based pagination. For large resources, the preview and count are separate reads, so concurrent qURL changes can make the preview length and \`qurl\_count\` briefly differ.

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

      - **`ai_agent_policy`**

        `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

        - **`allow_categories`**

          `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

        - **`block_all`**

          `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

        - **`deny_categories`**

          `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`geo_allowlist`**

        `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`geo_denylist`**

        `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`ip_allowlist`**

        `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`ip_denylist`**

        `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`user_agent_allow_regex`**

        `string` — Regular expression to allow matching user agents (max 256 characters)

      - **`user_agent_deny_regex`**

        `string` — Regular expression to deny matching user agents (max 256 characters)

    - **`created_at`**

      `string`, format: `date-time`

    - **`expires_at`**

      `string`, format: `date-time`

    - **`label`**

      `string`

    - **`max_sessions`**

      `integer`

    - **`one_time_use`**

      `boolean`

    - **`qurl_id`**

      `string`

    - **`qurl_site`**

      `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

    - **`session_duration`**

      `integer` — Session lifetime in seconds (0 = server default)

    - **`status`**

      `string`, possible values: `"active", "consumed", "expired", "revoked"`

    - **`target_path`**

      `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

    - **`use_count`**

      `integer`

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

  - **`slug`**

    `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. See \`ResourceData.slug\` for the full description — the field carries identical semantics on both response schemas.

  - **`status`**

    `string`, possible values: `"active", "revoked"` — Current lifecycle status. Computed at response time — resources past their expires\_at are reported as "expired" even if not explicitly revoked.

  - **`tags`**

    `array` — Tags for categorization and filtering

    **Items:**

    `string`

  - **`target_url`**

    `string`, format: `uri` — The protected backend URL. Optional — omitted (field absent from the JSON object, NOT serialized as null) when the resource is owned by a connector.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
      "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
      "target_url": "",
      "status": "active",
      "created_at": "",
      "expires_at": "",
      "description": "",
      "tags": [
        ""
      ],
      "qurl_site": "",
      "custom_domain": null,
      "slug": "",
      "qurl_count": 1,
      "qurls": [
        {
          "qurl_id": "",
          "label": "",
          "status": "active",
          "one_time_use": true,
          "max_sessions": 1,
          "session_duration": 1,
          "use_count": 1,
          "qurl_site": "",
          "target_path": "",
          "created_at": "",
          "...": "[Additional Properties Truncated]"
        }
      ]
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 304 Not Modified (ETag match)

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/qurls/{id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/qurls/{id}`

### Get a resource

- **Method:** `GET`
- **Path:** `/v1/qurls/{id}`
- **Tags:** QURLs

Returns a resource and a preview of up to 100 qURLs. Use its CRID as `id`. A qURL ID is also accepted and returns its parent resource. For every link, use `GET /v1/resources/{id}/qurls` and follow pagination.

#### Responses

##### Status: 200 Resource details

###### Content-Type: application/json

- **`data`**

  `object` — Resource details returned by the \`/v1/qurls\` compatibility operations. Use \`crid\` to identify the resource and \`qurl\_id\` to identify an individual link. The \`qurls\` array (present on single-get responses) is an unordered bounded preview (maximum 100) of per-qURL details including access policy and session limits. On list responses, only \`qurl\_count\` is populated.

  - **`created_at`**

    `string`, format: `date-time` — When the resource was created.

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`custom_domain`**

    `string | null` — Custom domain associated with this resource

  - **`description`**

    `string` — Human-readable description

  - **`expires_at`**

    `string`, format: `date-time` — When the resource expires. Each qURL can have its own earlier expiration.

  - **`qurl_count`**

    `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

  - **`qurl_site`**

    `string`, format: `uri` — Routing URL for the resource. It does not grant access by itself. Share a qURL access link with the recipient. Do not extract an identifier from this hostname.

  - **`qurls`**

    `array` — Unordered bounded preview of per-qURL details (maximum 100). Best-effort on single-get and omitted on list, preview lookup failure, or redacted resource types. This preview does not imply newest first or any stable ordering and is not active-first; an active qURL can be absent from the preview when earlier qURLs fill the cap. When present, \`qurl\_count\` is the exact total; if omitted, callers must treat truncation as unknown. For the complete per-resource qURL list, use \`GET /v1/resources/{id}/qurls\`, which pages through every qURL with cursor-based pagination. For large resources, the preview and count are separate reads, so concurrent qURL changes can make the preview length and \`qurl\_count\` briefly differ.

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

      - **`ai_agent_policy`**

        `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

        - **`allow_categories`**

          `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

        - **`block_all`**

          `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

        - **`deny_categories`**

          `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`geo_allowlist`**

        `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`geo_denylist`**

        `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`ip_allowlist`**

        `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`ip_denylist`**

        `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`user_agent_allow_regex`**

        `string` — Regular expression to allow matching user agents (max 256 characters)

      - **`user_agent_deny_regex`**

        `string` — Regular expression to deny matching user agents (max 256 characters)

    - **`created_at`**

      `string`, format: `date-time`

    - **`expires_at`**

      `string`, format: `date-time`

    - **`label`**

      `string`

    - **`max_sessions`**

      `integer`

    - **`one_time_use`**

      `boolean`

    - **`qurl_id`**

      `string`

    - **`qurl_site`**

      `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

    - **`session_duration`**

      `integer` — Session lifetime in seconds (0 = server default)

    - **`status`**

      `string`, possible values: `"active", "consumed", "expired", "revoked"`

    - **`target_path`**

      `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

    - **`use_count`**

      `integer`

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

  - **`slug`**

    `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. See \`ResourceData.slug\` for the full description — the field carries identical semantics on both response schemas.

  - **`status`**

    `string`, possible values: `"active", "revoked"` — Current lifecycle status. Computed at response time — resources past their expires\_at are reported as "expired" even if not explicitly revoked.

  - **`tags`**

    `array` — Tags for categorization and filtering

    **Items:**

    `string`

  - **`target_url`**

    `string`, format: `uri` — The protected backend URL. Optional — omitted (field absent from the JSON object, NOT serialized as null) when the resource is owned by a connector.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "target_url": "",
    "status": "active",
    "created_at": "",
    "expires_at": "",
    "description": "",
    "tags": [
      ""
    ],
    "qurl_site": "",
    "custom_domain": null,
    "slug": "",
    "qurl_count": 1,
    "qurls": [
      {
        "qurl_id": "",
        "label": "",
        "status": "active",
        "one_time_use": true,
        "max_sessions": 1,
        "session_duration": 1,
        "use_count": 1,
        "qurl_site": "",
        "target_path": "",
        "created_at": "",
        "...": "[Additional Properties Truncated]"
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 304 Not Modified (ETag match)

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Revoke a resource and all its qURLs

- **Method:** `DELETE`
- **Path:** `/v1/qurls/{id}`
- **Tags:** QURLs

Revokes a resource and blocks access through all its qURLs. Use the resource's CRID. To revoke one link, use `DELETE /v1/resources/{id}/qurls/{qurl_id}` instead. A qURL ID (`q_`) is not accepted by this resource-wide operation. Repeating this request does not emit another revoke webhook.

On `503`, revocation is saved but enforcement is still pending. Wait for `Retry-After`, then retry the same DELETE to confirm enforcement.

#### Responses

##### Status: 204 Resource and its qURLs revoked

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Update a resource

- **Method:** `PATCH`
- **Path:** `/v1/qurls/{id}`
- **Tags:** QURLs

Updates the resource description, tags, or expiration. Use its CRID as `id`. To update one link, use `PATCH /v1/resources/{id}/qurls/{qurl_id}`.

A revoked resource cannot be updated: the request returns `400 invalid_input` and changes nothing.

#### Request Body

##### Content-Type: application/json

- **`description`**

  `string` — Replace the resource description. Pass an empty string to clear.

- **`expires_at`**

  `string`, format: `date-time` — Absolute expiration time. Mutually exclusive with extend\_by. Must be in the future and within 30 days from now.

- **`extend_by`**

  `string` — Duration to extend by (e.g., "24h", "7d", "1w"). Minimum: 1 minute. Maximum: 30 days. Mutually exclusive with expires\_at.

- **`tags`**

  `array` — Replace all tags on this resource. Tags are lowercased and trimmed server-side. Duplicates are silently removed. Pass an empty array to clear all tags.

  **Items:**

  `string`

**Example:**

```json
{
  "extend_by": "1h",
  "expires_at": "",
  "tags": [
    ""
  ],
  "description": ""
}
```

#### Responses

##### Status: 200 Resource updated

###### Content-Type: application/json

- **`data`**

  `object` — Resource details returned by the \`/v1/qurls\` compatibility operations. Use \`crid\` to identify the resource and \`qurl\_id\` to identify an individual link. The \`qurls\` array (present on single-get responses) is an unordered bounded preview (maximum 100) of per-qURL details including access policy and session limits. On list responses, only \`qurl\_count\` is populated.

  - **`created_at`**

    `string`, format: `date-time` — When the resource was created.

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`custom_domain`**

    `string | null` — Custom domain associated with this resource

  - **`description`**

    `string` — Human-readable description

  - **`expires_at`**

    `string`, format: `date-time` — When the resource expires. Each qURL can have its own earlier expiration.

  - **`qurl_count`**

    `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

  - **`qurl_site`**

    `string`, format: `uri` — Routing URL for the resource. It does not grant access by itself. Share a qURL access link with the recipient. Do not extract an identifier from this hostname.

  - **`qurls`**

    `array` — Unordered bounded preview of per-qURL details (maximum 100). Best-effort on single-get and omitted on list, preview lookup failure, or redacted resource types. This preview does not imply newest first or any stable ordering and is not active-first; an active qURL can be absent from the preview when earlier qURLs fill the cap. When present, \`qurl\_count\` is the exact total; if omitted, callers must treat truncation as unknown. For the complete per-resource qURL list, use \`GET /v1/resources/{id}/qurls\`, which pages through every qURL with cursor-based pagination. For large resources, the preview and count are separate reads, so concurrent qURL changes can make the preview length and \`qurl\_count\` briefly differ.

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

      - **`ai_agent_policy`**

        `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

        - **`allow_categories`**

          `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

        - **`block_all`**

          `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

        - **`deny_categories`**

          `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`geo_allowlist`**

        `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`geo_denylist`**

        `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`ip_allowlist`**

        `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`ip_denylist`**

        `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`user_agent_allow_regex`**

        `string` — Regular expression to allow matching user agents (max 256 characters)

      - **`user_agent_deny_regex`**

        `string` — Regular expression to deny matching user agents (max 256 characters)

    - **`created_at`**

      `string`, format: `date-time`

    - **`expires_at`**

      `string`, format: `date-time`

    - **`label`**

      `string`

    - **`max_sessions`**

      `integer`

    - **`one_time_use`**

      `boolean`

    - **`qurl_id`**

      `string`

    - **`qurl_site`**

      `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

    - **`session_duration`**

      `integer` — Session lifetime in seconds (0 = server default)

    - **`status`**

      `string`, possible values: `"active", "consumed", "expired", "revoked"`

    - **`target_path`**

      `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

    - **`use_count`**

      `integer`

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

  - **`slug`**

    `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. See \`ResourceData.slug\` for the full description — the field carries identical semantics on both response schemas.

  - **`status`**

    `string`, possible values: `"active", "revoked"` — Current lifecycle status. Computed at response time — resources past their expires\_at are reported as "expired" even if not explicitly revoked.

  - **`tags`**

    `array` — Tags for categorization and filtering

    **Items:**

    `string`

  - **`target_url`**

    `string`, format: `uri` — The protected backend URL. Optional — omitted (field absent from the JSON object, NOT serialized as null) when the resource is owned by a connector.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "target_url": "",
    "status": "active",
    "created_at": "",
    "expires_at": "",
    "description": "",
    "tags": [
      ""
    ],
    "qurl_site": "",
    "custom_domain": null,
    "slug": "",
    "qurl_count": 1,
    "qurls": [
      {
        "qurl_id": "",
        "label": "",
        "status": "active",
        "one_time_use": true,
        "max_sessions": 1,
        "session_duration": 1,
        "use_count": 1,
        "qurl_site": "",
        "target_path": "",
        "created_at": "",
        "...": "[Additional Properties Truncated]"
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/qurls/{id}/mint\_link

- **Method:** `PARAMETERS`
- **Path:** `/v1/qurls/{id}/mint_link`

### Mint an access link

- **Method:** `POST`
- **Path:** `/v1/qurls/{id}/mint_link`
- **Tags:** QURLs

Creates another access link for an existing resource. Use its CRID as `id`. For new integrations, prefer `POST /v1/resources/{id}/qurls`, which keeps resource and link operations together. This route remains available for existing clients.

Send a JSON body; `{}` uses the defaults described in `MintLinkRequest`. Save `qurl_id` to manage the link and share `qurl_link` with the recipient. Use `Idempotency-Key` to recover a lost response.

For qURL Connectors, `target_path` can restrict access to a path. Null or non-string values return `400 validation_error`; an empty, malformed, or oversized path, or use on another resource type, returns `400 invalid_target_path`.

#### Request Body

##### Content-Type: application/json

- **`access_policy`**

  `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

  - **`ai_agent_policy`**

    `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

    - **`allow_categories`**

      `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`block_all`**

      `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

    - **`deny_categories`**

      `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

  - **`geo_allowlist`**

    `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`geo_denylist`**

    `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`ip_allowlist`**

    `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`ip_denylist`**

    `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`user_agent_allow_regex`**

    `string` — Regular expression to allow matching user agents (max 256 characters)

  - **`user_agent_deny_regex`**

    `string` — Regular expression to deny matching user agents (max 256 characters)

- **`expires_at`**

  `string`, format: `date-time` — Absolute expiration timestamp. Must be in the future and within 30 days from now. Mutually exclusive with expires\_in.

- **`expires_in`**

  `string` — Duration until expiration. Supports: minutes (5m), hours (24h), days (7d), weeks (1w). Minimum: 1 minute. Maximum: 30 days.

- **`label`**

  `string` — Human-readable label identifying who this qURL is for

- **`max_sessions`**

  `integer`, default: `0` — Maximum concurrent sessions for this qURL token (0 = unlimited)

- **`one_time_use`**

  `boolean` — Whether the link is consumed by its first successful use. Explicit \`true\` or \`false\` takes precedence over the resource defaults. When omitted, URL and integration-owned upload links use \`false\`. qURL Connector links use \`true\` unless \`max\_sessions\` is greater than 1. For a reusable Connector link, explicitly send \`one\_time\_use: false\`, including when \`max\_sessions\` is \`0\` (unlimited).

- **`session_duration`**

  `string` — How long a session permits new requests after the recipient first reaches the content. Minimum: 1 second. Maximum: 24 hours. If the parent resource was created with a \`session\_duration\` (see \`ResourceData.session\_duration\_cap\`), values exceeding that cap are rejected with HTTP 400 \`invalid\_session\_duration\`. Omitting this field on a capped resource inherits the cap; without a cap, qURL Connectors use 1 hour and other resource types use the service default. New requests are denied at session expiry. Active requests, streams, and WebSockets are canceled at that deadline. See \`CreateQurlRequest.session\_duration\` → \*\*Timing precision\*\* for the full semantics.

- **`target_path`**

  `string` — Destination path and optional query for a qURL Connector link. For example, \`/view/abc?download=1\` redirects to that path on the resource's \`qurl.site\` host. Omit this field to use the host's root. Only qURL Connector resources (\`type=tunnel\`) accept it. Maximum: 2048 UTF-8 bytes. \*\*Access scope:\*\* The path permits itself and descendants at a segment boundary. \`/view/abc\` permits \`/view/abc/thumbnail\`, but not \`/view/abcd\`. The query does not restrict access. A scoped link works only through the resource host; access through a custom domain is denied. The Connector must also authorize the path before using it to look up an object. \*\*Path rules:\*\* Start with one \`/\`. Use canonical, unescaped ASCII. A single trailing slash is allowed. Do not use \`//\`, percent escapes, semicolons, empty interior segments, \`.\` or \`..\` segments, exclamation marks, parentheses, asterisks, apostrophes, a scheme, host, backslash, fragment, whitespace, or control characters. Use opaque route keys for objects; keep user filenames in metadata. Clients must preserve the accepted path bytes. Percent-encoded alternatives are denied. \*\*Query rules:\*\* Use ASCII letters, digits, \`.\_\~!$&()\*+,;=:@/?-\`, and well-formed percent escapes. Percent-encode other characters. Literal apostrophes are not accepted. Query bytes are preserved, including semicolons and percent escapes. \*\*Errors:\*\* JSON null and non-string values return \`400 validation\_error\`. Empty strings, oversized or malformed paths, and use on other resource types return \`400 invalid\_target\_path\`.

**Example:**

```json
{
  "expires_in": "5m",
  "expires_at": "",
  "label": "Alice from Acme",
  "one_time_use": true,
  "max_sessions": 0,
  "session_duration": "1h",
  "access_policy": {
    "ip_allowlist": [
      "192.168.1.0/24",
      "10.0.0.1"
    ],
    "ip_denylist": [
      ""
    ],
    "geo_allowlist": [
      "US",
      "CA",
      "GB"
    ],
    "geo_denylist": [
      "CN",
      "RU"
    ],
    "user_agent_allow_regex": "^Mozilla.*",
    "user_agent_deny_regex": ".*bot.*",
    "ai_agent_policy": {
      "block_all": false,
      "deny_categories": [
        "gptbot",
        "commoncrawl",
        "bytedance"
      ],
      "allow_categories": [
        "claude",
        "chatgpt",
        "perplexity"
      ]
    }
  },
  "target_path": "/view/9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}
```

#### Responses

##### Status: 201 Access link created

###### Content-Type: application/json

- **`data`**

  `object`

  - **`branded_domain`**

    `string` — Custom hostname to display as the link text, for example \`customer.com\`. Use \`\<a href="{qurl\_link}">{branded\_domain}\</a>\`; keep \`qurl\_link\` as the destination. Returned only when the domain belongs to your account and has status \`verified\`, \`provisioning\_tls\`, or \`active\`. Otherwise omitted. During \`provisioning\_tls\`, the hostname might not serve HTTPS yet; use it only as display text until \`active\`.

  - **`expires_at`**

    `string`, format: `date-time`

  - **`qurl_id`**

    `string` — Identifier for this qURL. Save it to manage this link and match \`qurl.accessed\` webhook events.

  - **`qurl_link`**

    `string`, format: `uri` — Access link to share with the recipient. Its use limit is set by the request and resource defaults.

  - **`target_path`**

    `string` — Redirect destination and authorization scope for this new qURL on a qURL Connector resource. Omitted when the new qURL uses the bare origin. Each mint call makes an independent qURL; omission affects only that qURL.

  - **`type`**

    `string` — Resource type — echoes the type of the underlying resource.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "qurl_id": "q_a1b2c3d4e5f",
    "qurl_link": "",
    "branded_domain": "",
    "expires_at": "",
    "target_path": "",
    "type": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/qurls/batch

- **Method:** `PARAMETERS`
- **Path:** `/v1/qurls/batch`

### Batch create qURLs

- **Method:** `POST`
- **Path:** `/v1/qurls/batch`
- **Tags:** QURLs

Creates up to 100 qURLs in one request. Each item is a `CreateQurlRequest`.

| Status | Result                                                                                                                                                                                                               |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `201`  | Every item succeeded.                                                                                                                                                                                                |
| `207`  | Some items succeeded. Read each result before retrying.                                                                                                                                                              |
| `400`  | Every item failed, and each result says why. Or request validation rejected the batch.                                                                                                                               |
| `403`  | The account's enforced monthly data-transfer allowance is exhausted.                                                                                                                                                 |
| `500`  | An error on our side stopped the batch before any item succeeded. The response has no per-item results. An item that has a problem of its own is not reported either; it is reported when the request is sent again. |
| `503`  | The plan-limit check or billing is temporarily unavailable. No item was created, and the response has no per-item results. Wait for the time in `Retry-After`, then retry the unchanged request.                     |

A `400` reports failures item by item. A `500` or a `503` means that the batch failed as a whole: no item was created.

**Validation:** Invalid request shapes, unknown fields, and schema limits return one `400` error for the request. If per-item input validation fails, no items are created; valid items are marked as skipped in the per-item results.

**Quota:** Only items that create a new protected resource use the protected-resource allowance. Items that reuse a resource do not use that allowance, but can still fail for other reasons.

**Retries:** Match results to input items with `index`. Do not recreate successful items. Retry failed items with `service_unavailable`, and failed items with `creation_failed` whose `message` is `failed to create qURL`. Do not retry an item whose `message` says `the outcome is unknown` without a check: its qURL may exist. A `quota_exceeded` result requires resolving the plan limit before retrying. To recover a lost batch response, retry the unchanged request with its original `Idempotency-Key`. A `500` or `503` response is not stored for replay: retry the unchanged request, and it runs again even with the same `Idempotency-Key`. A `207` or `400` response is stored: to run failed items again, send them in a new request with a new `Idempotency-Key`.

#### Request Body

##### Content-Type: application/json

- **`items` (required)**

  `array` — Array of qURL creation requests (1-100 items)

  **Items:**

  - **`access_policy`**

    `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

    - **`ai_agent_policy`**

      `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

      - **`allow_categories`**

        `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`block_all`**

        `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

      - **`deny_categories`**

        `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`geo_allowlist`**

      `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`geo_denylist`**

      `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`ip_allowlist`**

      `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`ip_denylist`**

      `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`user_agent_allow_regex`**

      `string` — Regular expression to allow matching user agents (max 256 characters)

    - **`user_agent_deny_regex`**

      `string` — Regular expression to deny matching user agents (max 256 characters)

  - **`custom_domain`**

    `string` — Optional custom domain to assign to the auto-created resource. The domain must be registered, active, and owned by the caller. Only allowed when the target URL creates a new resource — rejected if a resource already exists for this target URL.

  - **`expires_in`**

    `string` — How long the new link remains valid. Examples: \`5m\`, \`1h\`, \`7d\`, \`1w\`. Minimum: 1 minute. Maximum: 30 days on all plans. The resource's expiration can shorten the link's lifetime; read \`expires\_at\` in the response. When omitted or empty, qURL Connector links use 5 minutes. URL and integration-owned upload links use the service default. Set this field explicitly for a predictable lifetime.

  - **`label`**

    `string` — Human-readable label identifying who this qURL is for

  - **`max_sessions`**

    `integer` — Maximum concurrent sessions for this qURL, not the whole resource. Use \`0\` for no limit or \`1\`–\`1000\` for a limit. Set \`one\_time\_use: false\` explicitly when you need multiple visitors: some resource types otherwise create a single-visitor link even when \`max\_sessions\` is \`0\`. Visitors that share a source IP count as one session and share its IP policy checks.

  - **`one_time_use`**

    `boolean` — Whether the link is consumed by its first successful use. Explicit \`true\` or \`false\` takes precedence over the resource defaults. When omitted, URL and integration-owned upload links use \`false\`. qURL Connector links use \`true\` unless \`max\_sessions\` is greater than 1. For a reusable Connector link, explicitly send \`one\_time\_use: false\`, including when \`max\_sessions\` is \`0\` (unlimited).

  - **`private`**

    `boolean` — Privacy of the resource, used only when this request creates it. When omitted, a new resource is private. Send \`false\` to create a public resource. Privacy cannot change after creation. When the request reuses your existing resource for the same \`target\_url\`, omit this field: the resource keeps the privacy it has. A value that differs from the existing resource returns \`400 privacy\_mismatch\`.

  - **`session_duration`**

    `string` — How long a session permits new requests after the recipient first reaches the content. This is separate from \`expires\_in\`, which controls the link's expiration. \*\*Default and limits:\*\* Supports seconds (\`30s\`), minutes (\`5m\`), hours (\`1h\`), and days (\`1d\`). Minimum: 1 second. Maximum: 24 hours. If omitted, uses the resource cap when set. Without a cap, qURL Connectors use 1 hour; other resource types use the service default. Set this field explicitly when timing matters. \*\*Resource cap:\*\* On the first create for a target URL, an explicit value also sets the resource's fixed \`session\_duration\_cap\`. Later creates, mints, and updates cannot exceed it (\`400 invalid\_session\_duration\`). See \`ResourceData.session\_duration\_cap\` for how to create a resource with a different cap. \*\*Timing precision:\*\* For signed browser qURLs with a configured duration, the first content request must be authorized within 10 seconds of the start of admission, including time spent completing admission. The session timer then runs for \`session\_duration\` from that first authorization. Initial network admission lasts \`session\_duration + 10 seconds\`; it does not double for long sessions. A 24-hour session gets 24 hours plus 10 seconds of initial network admission. Opening the original invitation again is not required during a live session, including after a TCP connection becomes idle. When duration is omitted on an uncapped URL resource, the existing default authorization deadline is 24 hours plus 10 seconds from admission; the configured-duration first-arrival rule does not apply. New requests are denied at session expiry. Active requests, streams, and WebSockets are canceled at that deadline. The network allowance does not extend application authorization. A single-use invitation remains consumed if the first content request misses the 10-second arrival deadline. Cancellation cannot retract data already delivered or buffered by the client.

  - **`target_url`**

    `string`, format: `uri` — The URL to protect with qURL (max 2048 characters). Required for standard reverse-proxy qURLs.

  - **`type`**

    `string`, default: `"url"` — Type of resource the qURL grants access to. Defaults to \`url\`; other values are restricted to specific integrations. See \`ResourceType\`.

**Example:**

```json
{
  "items": [
    {
      "type": "url",
      "private": true,
      "target_url": "https://dashboard.example.com",
      "expires_in": "1h",
      "one_time_use": true,
      "max_sessions": 0,
      "session_duration": "1h",
      "access_policy": {
        "ip_allowlist": [
          "192.168.1.0/24",
          "10.0.0.1"
        ],
        "ip_denylist": [
          ""
        ],
        "geo_allowlist": [
          "US",
          "CA",
          "GB"
        ],
        "geo_denylist": [
          "CN",
          "RU"
        ],
        "user_agent_allow_regex": "^Mozilla.*",
        "user_agent_deny_regex": ".*bot.*",
        "ai_agent_policy": {
          "block_all": false,
          "deny_categories": [
            "gptbot",
            "commoncrawl",
            "bytedance"
          ],
          "allow_categories": [
            "claude",
            "chatgpt",
            "perplexity"
          ]
        }
      },
      "label": "Alice from Acme",
      "custom_domain": "app.example.com"
    }
  ]
}
```

#### Responses

##### Status: 201 All qURLs created successfully

###### Content-Type: application/json

- **`data`**

  `object`

  - **`failed`**

    `integer` — Number of failed creations

  - **`results`**

    `array`

    **Items:**

    - **`branded_domain`**

      `string` — Customer's bare branded hostname (if success). See \`MintLinkData.branded\_domain\` for the canonical description, including the \`\<a href="{qurl\_link}">{branded\_domain}\</a>\` rendering pattern, the \`provisioning\_tls\` HTTPS-not-yet-live caveat, and the live-status + tenant-ownership gating.

    - **`crid`**

      `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

    - **`error`**

      `object` — Error details (if failed)

      - **`code`**

        `string`

      - **`message`**

        `string`

    - **`expires_at`**

      `string`, format: `date-time` — Expiration time (if success)

    - **`index`**

      `integer` — Original index in the request array

    - **`qurl_link`**

      `string`, format: `uri` — Access link (if success)

    - **`qurl_site`**

      `string`, format: `uri` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

    - **`resource_id`**

      `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

    - **`success`**

      `boolean`

  - **`succeeded`**

    `integer` — Number of successfully created qURLs

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "succeeded": 1,
    "failed": 1,
    "results": [
      {
        "index": 1,
        "success": true,
        "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
        "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
        "qurl_link": "",
        "branded_domain": "",
        "qurl_site": "",
        "expires_at": "",
        "error": {
          "code": "",
          "message": ""
        }
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 207 Some items succeeded and some failed. Match each result to its input with \`index\`. | Item error code | What to do | |---|---| | \`resource\_type\_conflict\` | Use the existing resource's CRID with \`POST /v1/resources/{id}/qurls\`, or choose another target URL. Revoking the existing resource also blocks its links. | | \`privacy\_mismatch\` | The item states \`private\`, and your existing resource for its \`target\_url\` has the other value. Privacy is fixed at creation. Omit \`private\` from the item to reuse that resource. | | \`quota\_exceeded\` | Resolve the plan limit before retrying. | | \`service\_unavailable\` | Retry only this failed item. When the resource for its \`target\_url\` was closed during the request, the retry may create a new resource for that \`target\_url\`. | | \`creation\_failed\` | Read \`message\`. There are three cases. The fixed text \`failed to create qURL\`: the item is not wrong and nothing was created for it, so retry it unchanged. A message that says \`the outcome is unknown\`: the qURL may have been created, and a retry creates another one, so check your qURLs for that target before you retry. Any other message names a problem in the item: correct the item before retrying. | Use a new \`Idempotency-Key\` when retrying a changed batch of failed items.

###### Content-Type: application/json

- **`data`**

  `object`

  - **`failed`**

    `integer` — Number of failed creations

  - **`results`**

    `array`

    **Items:**

    - **`branded_domain`**

      `string` — Customer's bare branded hostname (if success). See \`MintLinkData.branded\_domain\` for the canonical description, including the \`\<a href="{qurl\_link}">{branded\_domain}\</a>\` rendering pattern, the \`provisioning\_tls\` HTTPS-not-yet-live caveat, and the live-status + tenant-ownership gating.

    - **`crid`**

      `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

    - **`error`**

      `object` — Error details (if failed)

      - **`code`**

        `string`

      - **`message`**

        `string`

    - **`expires_at`**

      `string`, format: `date-time` — Expiration time (if success)

    - **`index`**

      `integer` — Original index in the request array

    - **`qurl_link`**

      `string`, format: `uri` — Access link (if success)

    - **`qurl_site`**

      `string`, format: `uri` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

    - **`resource_id`**

      `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

    - **`success`**

      `boolean`

  - **`succeeded`**

    `integer` — Number of successfully created qURLs

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "succeeded": 1,
    "failed": 1,
    "results": [
      {
        "index": 1,
        "success": true,
        "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
        "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
        "qurl_link": "",
        "branded_domain": "",
        "qurl_site": "",
        "expires_at": "",
        "error": {
          "code": "",
          "message": ""
        }
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Every item failed, or request validation rejected the batch. A batch that failed as a whole on our side answers \`500\` or \`503\` instead, with no per-item results. Per-item \`code\` field on each failed item carries the same vocabulary as the 207 response (\`resource\_type\_conflict\`, \`privacy\_mismatch\`, \`creation\_failed\`, \`quota\_exceeded\`, or \`service\_unavailable\`). SDK consumers should branch on per-item codes rather than the outer HTTP status. In particular, a batch in which every item was refused by a plan limit answers 400 with \`quota\_exceeded\` on each item — not 403, because only this response shape carries the per-item results.

###### Content-Type: application/json

- **`data`**

  `object`

  - **`failed`**

    `integer` — Number of failed creations

  - **`results`**

    `array`

    **Items:**

    - **`branded_domain`**

      `string` — Customer's bare branded hostname (if success). See \`MintLinkData.branded\_domain\` for the canonical description, including the \`\<a href="{qurl\_link}">{branded\_domain}\</a>\` rendering pattern, the \`provisioning\_tls\` HTTPS-not-yet-live caveat, and the live-status + tenant-ownership gating.

    - **`crid`**

      `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

    - **`error`**

      `object` — Error details (if failed)

      - **`code`**

        `string`

      - **`message`**

        `string`

    - **`expires_at`**

      `string`, format: `date-time` — Expiration time (if success)

    - **`index`**

      `integer` — Original index in the request array

    - **`qurl_link`**

      `string`, format: `uri` — Access link (if success)

    - **`qurl_site`**

      `string`, format: `uri` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

    - **`resource_id`**

      `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

    - **`success`**

      `boolean`

  - **`succeeded`**

    `integer` — Number of successfully created qURLs

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "succeeded": 1,
    "failed": 1,
    "results": [
      {
        "index": 1,
        "success": true,
        "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
        "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
        "qurl_link": "",
        "branded_domain": "",
        "qurl_site": "",
        "expires_at": "",
        "error": {
          "code": "",
          "message": ""
        }
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/delegated-qurl-batches

- **Method:** `PARAMETERS`
- **Path:** `/v1/delegated-qurl-batches`

### Create qURLs from a delegated mint capability

- **Method:** `POST`
- **Path:** `/v1/delegated-qurl-batches`
- **Tags:** QURLs

Accepts one opaque delegated mint capability and a bounded set of independent recipient grants. The capability fixes the protected resource and destination path. The authenticated API key owns the new qURLs.

The request is accepted as one durable asynchronous operation. Poll the URL in `Location` until the batch reaches a terminal state. Retrying an accepted request with the same `Idempotency-Key` and the same semantic body returns the original operation acceptance, including after the capability expires. Reusing the key with a different body returns 409. This route requires API key authentication because the capability is bound to the authenticated key ID.

#### Request Body

##### Content-Type: application/json

- **`grants` (required)**

  `array` — Independent recipient grants, processed in input order

  **Items:**

  - **`expires_in`**

    `string` — qURL lifetime. The delegated capability sets the maximum. If omitted, the service uses the capability maximum.

  - **`label`**

    `string` — Human-readable recipient label, at most 500 UTF-8 bytes

  - **`max_sessions`**

    `integer` — Maximum sessions for this qURL; zero means unlimited

  - **`one_time_use`**

    `boolean`, default: `false` — Whether the qURL is consumed by its first admission

  - **`session_duration`**

    `string` — Access lifetime after a successful admission

- **`mint_capability` (required)**

  `string` — Opaque delegated mint capability returned by the upload service

**Example:**

```json
{
  "mint_capability": "",
  "grants": [
    {
      "expires_in": "1h",
      "label": "",
      "one_time_use": false,
      "max_sessions": 0,
      "session_duration": "1h"
    }
  ]
}
```

#### Responses

##### Status: 202 Batch accepted for asynchronous processing

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`batch_id` (required)**

    `string`

  - **`item_count` (required)**

    `integer`

  - **`status` (required)**

    `string`, possible values: `"queued"`

  - **`submitted_at` (required)**

    `string`, format: `date-time`

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "batch_id": "",
    "status": "queued",
    "item_count": 1,
    "submitted_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 The API key cannot redeem this capability, or the account quota refuses new qURLs

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 413 Request body exceeds the endpoint limit

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/delegated-qurl-batches/{batch\_id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/delegated-qurl-batches/{batch_id}`

### Get a delegated qURL batch

- **Method:** `GET`
- **Path:** `/v1/delegated-qurl-batches/{batch_id}`
- **Tags:** QURLs

Returns the current state and stable, input-ordered item results for a delegated qURL batch. This route requires an active API key for the same external binding that created the batch because a terminal result contains bearer share links. A non-terminal response includes `Retry-After`. Use `If-None-Match` to avoid receiving an unchanged non-terminal representation. Terminal responses always return `200` so exact retries can recover the result.

#### Responses

##### Status: 200 Terminal batch state

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`batch_id` (required)**

    `string`

  - **`item_count` (required)**

    `integer`

  - **`status` (required)**

    `string`, possible values: `"queued", "running", "succeeded", "partially_failed", "failed"`

  - **`submitted_at` (required)**

    `string`, format: `date-time`

  - **`completed_at`**

    `string`, format: `date-time`

  - **`results`**

    `array` — Results in the same order as the submitted grants

    **Items:**

    - **`index` (required)**

      `integer`

    - **`status` (required)**

      `string`, possible values: `"succeeded", "failed"`

    - **`error`**

      `object`

      - **`code` (required)**

        `string`, possible values: `"creation_failed"`

      - **`message` (required)**

        `string`

    - **`qurl`**

      `object`

      - **`expires_at` (required)**

        `string`, format: `date-time`

      - **`qurl_id` (required)**

        `string`

      - **`qurl_link` (required)**

        `string`, format: `uri` — Fresh share link returned only in the terminal batch result

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "batch_id": "",
    "status": "queued",
    "item_count": 1,
    "submitted_at": "",
    "completed_at": "",
    "results": [
      {
        "index": 0,
        "status": "succeeded",
        "qurl": {
          "qurl_id": "",
          "qurl_link": "",
          "expires_at": ""
        },
        "error": {
          "code": "creation_failed",
          "message": ""
        }
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 202 Non-terminal batch state

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`batch_id` (required)**

    `string`

  - **`item_count` (required)**

    `integer`

  - **`status` (required)**

    `string`, possible values: `"queued", "running", "succeeded", "partially_failed", "failed"`

  - **`submitted_at` (required)**

    `string`, format: `date-time`

  - **`completed_at`**

    `string`, format: `date-time`

  - **`results`**

    `array` — Results in the same order as the submitted grants

    **Items:**

    - **`index` (required)**

      `integer`

    - **`status` (required)**

      `string`, possible values: `"succeeded", "failed"`

    - **`error`**

      `object`

      - **`code` (required)**

        `string`, possible values: `"creation_failed"`

      - **`message` (required)**

        `string`

    - **`qurl`**

      `object`

      - **`expires_at` (required)**

        `string`, format: `date-time`

      - **`qurl_id` (required)**

        `string`

      - **`qurl_link` (required)**

        `string`, format: `uri` — Fresh share link returned only in the terminal batch result

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "batch_id": "",
    "status": "queued",
    "item_count": 1,
    "submitted_at": "",
    "completed_at": "",
    "results": [
      {
        "index": 0,
        "status": "succeeded",
        "qurl": {
          "qurl_id": "",
          "qurl_link": "",
          "expires_at": ""
        },
        "error": {
          "code": "creation_failed",
          "message": ""
        }
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 304 Batch state has not changed

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/delegated-qurls/{qurl\_id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/delegated-qurls/{qurl_id}`

### Get delegated qURL metadata

- **Method:** `GET`
- **Path:** `/v1/delegated-qurls/{qurl_id}`
- **Tags:** QURLs

Returns metadata for one delegated qURL owned by the authenticated account. This metadata operation does not return the share link. Retrieve it from the completed batch result.

#### Responses

##### Status: 200 Delegated qURL metadata

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`expires_at` (required)**

    `string`, format: `date-time`

  - **`max_sessions` (required)**

    `integer`

  - **`one_time_use` (required)**

    `boolean`

  - **`qurl_id` (required)**

    `string`

  - **`session_duration` (required)**

    `integer` — Session lifetime in seconds

  - **`status` (required)**

    `string`, possible values: `"active", "consumed", "expired", "revoked"`

  - **`label`**

    `string`

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "qurl_id": "",
    "status": "active",
    "expires_at": "",
    "label": "",
    "one_time_use": true,
    "max_sessions": 0,
    "session_duration": 0
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Revoke a delegated qURL

- **Method:** `DELETE`
- **Path:** `/v1/delegated-qurls/{qurl_id}`
- **Tags:** QURLs

Revokes one delegated qURL owned by the authenticated account and revokes its access grant. Active network access is closed before success is reported. Repeating the request is successful. A 503 `mutation_outcome_unknown` means the state can already be closed; retry this same DELETE until it returns 204.

#### Responses

##### Status: 204 Delegated qURL revoked

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources`

### List resources

- **Method:** `GET`
- **Path:** `/v1/resources`
- **Tags:** Resources

Lists your resources. Use each resource's `crid` for later requests. `qurl_count` includes retained revoked and expired qURLs; it is not an active-link count. An omitted count is unknown. Get a resource for a preview of its links, or use `GET /v1/resources/{id}/qurls` for the full paginated list.

| Filter        | Result and restrictions                                                                       |
| ------------- | --------------------------------------------------------------------------------------------- |
| `alias`       | Zero or one of your resources. Cannot combine with `slug`, `status`, or `type`.               |
| `slug`        | Zero or one of your active qURL Connectors. Cannot combine with `alias`, `status`, or `type`. |
| `type=url`    | Resources that protect target URLs.                                                           |
| `type=tunnel` | qURL Connector resources.                                                                     |

Alias and slug lookups ignore `cursor` and `limit`. Invalid or reserved aliases return `400 alias_invalid_format` or `400 alias_reserved`. Connector-owned uploads (`type=transit`) are not listed; use connector installation operations for their reports. Restricted `key_type=tunnel_bootstrap` credentials cannot list resources; use their bound `POST /v1/resources` find-or-create operation.

Keep `type` and `status` unchanged while paging. Changing them with an existing cursor returns `400 invalid_request`; restart from the first page instead.

#### Responses

##### Status: 200 Resource list

###### Content-Type: application/json

- **`data`**

  `array`

  **Items:**

  - **`access_requests`**

    `boolean` — Whether someone who opens this private resource's link without access can ask you for it. Always present. Change it with \`PATCH /v1/resources/{id}\`.

  - **`alias`**

    `string | null` — Per-owner human-readable handle for the resource (e.g. \`dev-dashboard\`). \`null\` when no alias is set. Lookup via \`GET /v1/resources?alias={alias}\` (returns a 0- or 1-item list).

  - **`allowed_device_keys`**

    `array` — Public X25519 keys of the devices that can create links for this private resource, in canonical padded standard base64. Omitted when no devices are listed; only the owner can then create links for a private resource. The list has no effect unless the resource is private.

    **Items:**

    `string`

  - **`allowed_passkeys`**

    `array` — The passkeys you approved for this private resource, oldest approval first. Always present; empty when you approved none. A passkey is added by approving an access request and removed with \`DELETE /v1/resources/{id}/allowed-passkeys/{device\_id}\`.

    **Items:**

    - **`approved_at` (required)**

      `string`, format: `date-time` — When you approved the request.

    - **`device_id` (required)**

      `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

    - **`name` (required)**

      `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

  - **`connector_routing_id`**

    `string` — Opaque routing label the qURL Connector must use verbatim; do not derive it client-side.

  - **`consumer_access`**

    `object`

    - **`mode` (required)**

      `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

    - **`version` (required)**

      `integer`, format: `int64`

    - **`contract_id`**

      `string` — Required only for contract mode. Names the resource-specific contract.

  - **`consumer_access_version`**

    `integer`, format: `int64` — Monotonic consumer policy revision. Zero means no consumer policy has been set.

  - **`created_at`**

    `string`, format: `date-time`

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`desired_state`**

    `string`, possible values: `"on", "off"`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`knock_resource_id`**

    `string` — Internal use.

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`private`**

    `boolean`, default: `false` — When true, only the owner and the devices listed in \`allowed\_device\_keys\` can create links for this resource. A resource that is not private is public. Privacy is chosen when the resource is created (a new resource is private unless its creator asked for a public one) and cannot change afterward. Older resources can omit this field; they are public.

  - **`publisher`**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`qurl_count`**

    `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

  - **`serving_epoch`**

    `integer`, format: `int64` — Monotonic qURL Connector serving lifecycle. Zero means sharing has never been started.

  - **`session_duration_cap`**

    `integer` — Maximum session duration, in seconds, for qURLs on this resource. It is fixed when \`POST /v1/qurls\` first creates the resource. Later link creation and updates cannot exceed it (\`400 invalid\_session\_duration\`). Omitted session durations inherit the cap. Absent means there is no resource cap; the 1-second to 24-hour field limits still apply. \`POST /v1/resources\` creates a resource without a cap, and later link creation does not add one. To change a cap, register a different target URL, or revoke this resource and recreate it through \`POST /v1/qurls\` with the required \`session\_duration\`. Revocation blocks all existing links. A new resource receives a new CRID.

  - **`slug`**

    `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. Distinct from \`alias\`: slugs are immutable post-create, aliases are mutable. Lookup via \`GET /v1/resources?slug={slug}\` returns a 0- or 1-item active-resource list. See \`CreateResourceRequest.slug\` for format, uniqueness, and \`find\_or\_create\` semantics.

  - **`status`**

    `string`, possible values: `"active", "revoked"`

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `string` — The URL to protect. Optional — omitted (field absent from the JSON object, NOT serialized as null) when not applicable to this resource, or when the resource is owned by a connector.

  - **`tombstoned_at`**

    `string`, format: `date-time` — When the resource's lifecycle closed. Present only on tombstoned resources; absent on active ones. Consumers fetching a single resource by ID handle the tombstoned-row case via the 410 response on \`GET /v1/resources/{id}\` — the same timestamp surfaces in \`meta.tombstone.tombstoned\_at\`. List / alias-lookup (\`GET /v1/resources?alias=...\`) responses include this field on the resource itself so callers can distinguish tombstoned from active without a per-row probe.

  - **`type`**

    `string` — Resource type (see \`ResourceType\` for the full enum). Management-API read responses on connector-owned upload rows redact user-content resource fields (\`target\_url\`, \`description\`, \`tags\`, \`custom\_domain\`, \`alias\`, \`preserve\_host\`, \`qurl\_count\`, \`slug\`) and nested qURL summary fields (\`label\`, \`target\_path\`). List/detail responses include the row but omit those fields.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "consumer_access_version": 1,
      "consumer_access": {
        "mode": "keys",
        "version": 1,
        "contract_id": ""
      },
      "private": false,
      "allowed_device_keys": [
        ""
      ],
      "access_requests": true,
      "allowed_passkeys": [
        {
          "device_id": "k3wp-x2aq-7mzn-4tby",
          "name": "",
          "approved_at": ""
        }
      ],
      "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
      "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
      "type": "",
      "target_url": "",
      "knock_resource_id": "",
      "connector_routing_id": "",
      "status": "active",
      "desired_state": "on",
      "serving_epoch": 0,
      "description": "",
      "tags": [
        ""
      ],
      "custom_domain": null,
      "alias": null,
      "slug": "",
      "preserve_host": false,
      "session_duration_cap": 30,
      "qurl_count": 1,
      "created_at": "",
      "expires_at": "",
      "publisher": {
        "name": "Acme Docs",
        "verified": false
      },
      "tombstoned_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Create a resource

- **Method:** `POST`
- **Path:** `/v1/resources`
- **Tags:** Resources

Registers a resource before you create links for it. Save the returned `crid`. For a URL resource, a repeated request with the same URL and account returns the existing resource with `201`. A different resource type returns `409 resource_type_conflict`.

For an existing URL resource, omit `alias` or send its current alias. A different alias, including a new alias when none is set, returns `409 resource_alias_mismatch`. Use `PATCH /v1/resources/{id}` with the CRID to change an existing alias. An alias used by another of your resources returns `409 alias_in_use`.

A new URL or qURL Connector resource is private unless the request sends `private: false`. Resource privacy cannot change after creation. A request that returns an existing resource keeps the privacy that resource has. If it states `private` with a different value, it returns `400 privacy_mismatch`: omit `private` to reuse the resource.

A request that returns an existing resource does not change its allowed devices either. If it states `allowed_device_keys` with other keys than the resource lists, it returns `400 device_keys_mismatch` and changes nothing: omit the field to reuse the resource, and change the list with `PATCH /v1/resources/{id}` (`allowed_device_keys_add`, `allowed_device_keys_remove`). When both differ, the answer is `privacy_mismatch`.

For qURL Connectors, use `slug` and `find_or_create`; see the request fields. `403 connector_disabled` means Connector operations are unavailable. Use `error.code` to distinguish it from an authorization failure.

#### Request Body

##### Content-Type: application/json

- **`access_requests`**

  `boolean` — When true, someone who opens this private resource's link without access can ask you for it. You approve or deny each request. When omitted, a new resource accepts no access requests. Only a private resource can accept access requests: \`true\` together with a public resource returns \`400 invalid\_input\`. When the request returns an existing resource, this field changes nothing: the response carries the value the resource has. Change it with \`PATCH /v1/resources/{id}\`.

- **`alias`**

  `string` — Optional human-readable handle for the resource, unique per owner. Format: 3–64 lowercase alphanumeric chars and hyphens, must start with a letter and end alphanumeric. Reserved words (e.g. \`admin\`, \`qurl\`, \`tunnel\`) are rejected with 400 \`alias\_reserved\`. Collisions with another of your resources return 409 \`alias\_in\_use\`. Disagreeing with the alias of an existing resource for the same target URL returns 409 \`resource\_alias\_mismatch\`.

- **`allowed_device_keys`**

  `array` — Public X25519 keys of the devices that can create links for this private resource, in canonical padded standard base64. Only the publisher can change this list; with an empty list only the owner can create links. The list has no effect unless the resource is private. When the request returns an existing resource, omit the list or send the keys that resource lists, in any order. Other keys return \`400 device\_keys\_mismatch\` and change nothing, because a create request does not change the allowed devices of a resource that exists. Change the list with \`PATCH /v1/resources/{id}\` (\`allowed\_device\_keys\_add\`, \`allowed\_device\_keys\_remove\`).

  **Items:**

  `string`

- **`consumer_access`**

  `object`

  - **`mode` (required)**

    `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`contract_id`**

    `string` — Required only for contract mode. Names the resource-specific contract.

- **`custom_domain`**

  `string` — Optional custom domain to bind to this resource. Must be a domain previously verified via the domains API.

- **`description`**

  `string`

- **`find_or_create`**

  `boolean`, default: `false` — Reuse an active qURL Connector with the same slug in your account. Default: \`false\`. Requires \`type=tunnel\` and \`slug\`; otherwise returns \`400\`. | Existing Connector | Result when true | |---|---| | Active | Return it with \`201\` and \`meta.found\_existing: true\`. | | None | Create one with \`201\` and \`meta.found\_existing: false\`. | | Revoked | Create a new resource with \`201\` and \`meta.found\_existing: false\`. The old CRID and links remain revoked. | | Closed | Return \`410\`; choose a new slug or delete the retained resource before retrying. | Reusing a Connector ignores \`alias\`, \`description\`, and \`tags\`; use PATCH to change them. Concurrent requests resolve to one resource. On \`409 slug\_in\_use\`, retry once, then inspect the existing Connector before deciding to revoke it. Success consumes a restricted Connector enrollment credential, including reuse. For recovery after \`500\`, \`401\`, or \`409 bootstrap\_key\_consumed\`, see \`CreateApiKeyRequest.kind\`.

- **`private`**

  `boolean` — When true, only the owner and the devices listed in \`allowed\_device\_keys\` can create links for this resource. A resource that is not private is public. When omitted, a new URL or qURL Connector resource is private. Send \`false\` to create a public one. Privacy cannot change after creation. When the request returns an existing resource, omit this field: the resource keeps the privacy it has. A value that differs from the existing resource returns \`400 privacy\_mismatch\`.

- **`slug`**

  `string` — Stable name for a qURL Connector within your account. Required for \`type=tunnel\`; forbidden for other resource types. Use 3–64 lowercase letters, digits, or hyphens, starting with a letter and ending with a letter or digit. There are no reserved names. A slug cannot change after creation. An \`alias\` can change and is intended for display. Use \`GET /v1/resources?slug={slug}\` to find the Connector after a process restart. Without \`find\_or\_create: true\`, an existing slug returns \`409 slug\_in\_use\`. See \`find\_or\_create\` for reuse and retry behavior. A missing slug returns \`400\` for account credentials, or \`403\` for a restricted Connector enrollment credential whose permission requires a bound slug. Use \`error.code\` to handle errors.

- **`tags`**

  `array`

  **Items:**

  `string`

- **`target_url`**

  `string`, format: `uri` — The URL to protect.

- **`type`**

  `string`, possible values: `"url", "tunnel", "transit"`, default: `"url"` — The kind of protected resource: | Value | Resource | |---|---| | \`url\` | A target URL. Default. | | \`tunnel\` | A qURL Connector, available to permitted Connector clients. | | \`transit\` | Content uploaded through an integration. | See \`ResourceData.type\` for fields omitted from integration-owned upload responses.

**Example:**

```json
{
  "consumer_access": {
    "mode": "keys",
    "version": 1,
    "contract_id": ""
  },
  "private": true,
  "access_requests": true,
  "allowed_device_keys": [
    ""
  ],
  "type": "url",
  "target_url": "",
  "description": "",
  "tags": [
    ""
  ],
  "custom_domain": "",
  "alias": "dev-dashboard",
  "slug": "prod-dashboard",
  "find_or_create": true
}
```

#### Responses

##### Status: 201 Resource created

###### Content-Type: application/json

- **`data`**

  `object`

  - **`access_requests`**

    `boolean` — Whether someone who opens this private resource's link without access can ask you for it. Always present. Change it with \`PATCH /v1/resources/{id}\`.

  - **`alias`**

    `string | null` — Per-owner human-readable handle for the resource (e.g. \`dev-dashboard\`). \`null\` when no alias is set. Lookup via \`GET /v1/resources?alias={alias}\` (returns a 0- or 1-item list).

  - **`allowed_device_keys`**

    `array` — Public X25519 keys of the devices that can create links for this private resource, in canonical padded standard base64. Omitted when no devices are listed; only the owner can then create links for a private resource. The list has no effect unless the resource is private.

    **Items:**

    `string`

  - **`allowed_passkeys`**

    `array` — The passkeys you approved for this private resource, oldest approval first. Always present; empty when you approved none. A passkey is added by approving an access request and removed with \`DELETE /v1/resources/{id}/allowed-passkeys/{device\_id}\`.

    **Items:**

    - **`approved_at` (required)**

      `string`, format: `date-time` — When you approved the request.

    - **`device_id` (required)**

      `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

    - **`name` (required)**

      `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

  - **`connector_routing_id`**

    `string` — Opaque routing label the qURL Connector must use verbatim; do not derive it client-side.

  - **`consumer_access`**

    `object`

    - **`mode` (required)**

      `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

    - **`version` (required)**

      `integer`, format: `int64`

    - **`contract_id`**

      `string` — Required only for contract mode. Names the resource-specific contract.

  - **`consumer_access_version`**

    `integer`, format: `int64` — Monotonic consumer policy revision. Zero means no consumer policy has been set.

  - **`created_at`**

    `string`, format: `date-time`

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`desired_state`**

    `string`, possible values: `"on", "off"`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`knock_resource_id`**

    `string` — Internal use.

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`private`**

    `boolean`, default: `false` — When true, only the owner and the devices listed in \`allowed\_device\_keys\` can create links for this resource. A resource that is not private is public. Privacy is chosen when the resource is created (a new resource is private unless its creator asked for a public one) and cannot change afterward. Older resources can omit this field; they are public.

  - **`publisher`**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`qurl_count`**

    `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

  - **`serving_epoch`**

    `integer`, format: `int64` — Monotonic qURL Connector serving lifecycle. Zero means sharing has never been started.

  - **`session_duration_cap`**

    `integer` — Maximum session duration, in seconds, for qURLs on this resource. It is fixed when \`POST /v1/qurls\` first creates the resource. Later link creation and updates cannot exceed it (\`400 invalid\_session\_duration\`). Omitted session durations inherit the cap. Absent means there is no resource cap; the 1-second to 24-hour field limits still apply. \`POST /v1/resources\` creates a resource without a cap, and later link creation does not add one. To change a cap, register a different target URL, or revoke this resource and recreate it through \`POST /v1/qurls\` with the required \`session\_duration\`. Revocation blocks all existing links. A new resource receives a new CRID.

  - **`slug`**

    `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. Distinct from \`alias\`: slugs are immutable post-create, aliases are mutable. Lookup via \`GET /v1/resources?slug={slug}\` returns a 0- or 1-item active-resource list. See \`CreateResourceRequest.slug\` for format, uniqueness, and \`find\_or\_create\` semantics.

  - **`status`**

    `string`, possible values: `"active", "revoked"`

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `string` — The URL to protect. Optional — omitted (field absent from the JSON object, NOT serialized as null) when not applicable to this resource, or when the resource is owned by a connector.

  - **`tombstoned_at`**

    `string`, format: `date-time` — When the resource's lifecycle closed. Present only on tombstoned resources; absent on active ones. Consumers fetching a single resource by ID handle the tombstoned-row case via the 410 response on \`GET /v1/resources/{id}\` — the same timestamp surfaces in \`meta.tombstone.tombstoned\_at\`. List / alias-lookup (\`GET /v1/resources?alias=...\`) responses include this field on the resource itself so callers can distinguish tombstoned from active without a per-row probe.

  - **`type`**

    `string` — Resource type (see \`ResourceType\` for the full enum). Management-API read responses on connector-owned upload rows redact user-content resource fields (\`target\_url\`, \`description\`, \`tags\`, \`custom\_domain\`, \`alias\`, \`preserve\_host\`, \`qurl\_count\`, \`slug\`) and nested qURL summary fields (\`label\`, \`target\_path\`). List/detail responses include the row but omit those fields.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "consumer_access_version": 1,
    "consumer_access": {
      "mode": "keys",
      "version": 1,
      "contract_id": ""
    },
    "private": false,
    "allowed_device_keys": [
      ""
    ],
    "access_requests": true,
    "allowed_passkeys": [
      {
        "device_id": "k3wp-x2aq-7mzn-4tby",
        "name": "",
        "approved_at": ""
      }
    ],
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "type": "",
    "target_url": "",
    "knock_resource_id": "",
    "connector_routing_id": "",
    "status": "active",
    "desired_state": "on",
    "serving_epoch": 0,
    "description": "",
    "tags": [
      ""
    ],
    "custom_domain": null,
    "alias": null,
    "slug": "",
    "preserve_host": false,
    "session_duration_cap": 30,
    "qurl_count": 1,
    "created_at": "",
    "expires_at": "",
    "publisher": {
      "name": "Acme Docs",
      "verified": false
    },
    "tombstoned_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Forbidden while creating a resource; inspect error.code

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 Use \`error.code\` to choose the next action: | Code | Next action | |---|---| | \`slug\_in\_use\` | Choose another slug, or set \`find\_or\_create: true\` to reuse an active Connector. If that request still conflicts, retry once, then inspect the existing Connector. Revoking it blocks its links. | | \`alias\_in\_use\` | Choose another alias, or clear the alias on the resource that currently uses it. | | \`resource\_alias\_mismatch\` | Use PATCH to change the existing resource's alias. A create request does not rename it. | | \`resource\_type\_conflict\` | Reuse the existing resource with its current type, or revoke it before creating a replacement. Revocation blocks its links. | | \`bootstrap\_key\_consumed\` | Obtain a new enrollment credential and check the resource with an account credential. The previous action can have partly succeeded. |

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 410 Returned by the qURL Connector \`find\_or\_create\` flow when the supplied \`slug\` resolves to a tombstoned resource. The body carries the standard problem-details envelope plus \`meta.tombstone\` when the tombstone metadata can be loaded. Delete the tombstoned resource or choose a fresh slug before retrying.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/connectors/installations

- **Method:** `PARAMETERS`
- **Path:** `/v1/connectors/installations`

### List connector installations

- **Method:** `GET`
- **Path:** `/v1/connectors/installations`
- **Tags:** Connectors

Lists your connected integrations, such as Slack workspaces, Discord guilds, or storage accounts, with resource, qURL, and access counts. Recent activity can take time to appear in these counts.

#### Responses

##### Status: 200 Connector installation list

###### Content-Type: application/json

- **`data` (required)**

  `array`

  **Items:**

  - **`capabilities` (required)**

    `object`

    - **`configure` (required)**

      `boolean`

    - **`disconnect` (required)**

      `boolean`

    - **`reauth` (required)**

      `boolean`

    - **`view_activity` (required)**

      `boolean`

  - **`installation_id` (required)**

    `string` — Stable identifier for this connector installation.

  - **`installed_at` (required)**

    `string`, format: `date-time`

  - **`label` (required)**

    `string` — Human-readable label for this installation.

  - **`plugin_id` (required)**

    `string` — Connector plugin identifier.

  - **`stats` (required)**

    `object`

    - **`accesses_24h` (required)**

      `integer`, format: `int64` — Successful accesses in the last 24 hours.

    - **`accesses_7d` (required)**

      `integer`, format: `int64` — Successful accesses in the last 7 days.

    - **`errors_24h` (required)**

      `integer`, format: `int64` — Failed connector actions in the last 24 hours.

    - **`qurls` (required)**

      `integer`, format: `int64` — Number of active qURLs associated with this installation.

    - **`resources` (required)**

      `integer`, format: `int64` — Number of protected resources associated with this installation.

  - **`status` (required)**

    `string`, possible values: `"active", "degraded", "disconnected", "needs_reauth"`

  - **`subject_display_name` (required)**

    `string` — Display name for the connected workspace, guild, bucket, or account.

  - **`subject_kind` (required)**

    `string` — Connector-owned subject type.

  - **`last_activity_at`**

    `string | null`, format: `date-time`

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "installation_id": "",
      "plugin_id": "slack",
      "label": "Engineering Slack",
      "subject_kind": "slack_workspace",
      "subject_display_name": "LayerV Engineering",
      "status": "active",
      "installed_at": "",
      "last_activity_at": null,
      "stats": {
        "resources": 1,
        "qurls": 1,
        "accesses_24h": 1,
        "accesses_7d": 1,
        "errors_24h": 1
      },
      "capabilities": {
        "configure": true,
        "disconnect": true,
        "reauth": true,
        "view_activity": true
      }
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Authorize device credential recovery

- **Method:** `POST`
- **Path:** `/v1/connectors/agents/{agent_id}/recovery-token`
- **Tags:** Connectors

Authorizes one recovery of a revoked device credential while retaining its identity and existing shares. Requires a user OAuth access token and an ID token proving a fresh sign-in after credential revocation. The device must belong directly to the signing-in user; selected device or organization owners are not supported. The caller generates and retains the recovery secret; only its SHA-256 hash is registered. Exact retries return the original expiry. Retain the original request for retries; an already authorized attempt does not require another fresh sign-in. A new authorization attempt requires a new secret and Idempotency-Key; registered secret hashes cannot be reused, even after expiry. If the capability expires unused, confirm no native Issue is pending before starting a new authorization. Preserve the secret and original request while native Issue is pending.

#### Request Body

##### Content-Type: application/json

- **`expected_device_key_id` (required)**

  `string`

- **`reauthentication_proof` (required)**

  `string`

- **`recovery_token_sha256` (required)**

  `string`

**Example:**

```json
{
  "expected_device_key_id": "",
  "recovery_token_sha256": "",
  "reauthentication_proof": ""
}
```

#### Responses

##### Status: 200 Exact recovery attempt replayed

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`expires_at` (required)**

    `string`, format: `date-time` — Deadline for starting recovery with the retained secret.

  - **`recovery_token_id` (required)**

    `string` — Identifier of the recovery authorization, not the recovery secret.

**Example:**

```json
{
  "data": {
    "recovery_token_id": "",
    "expires_at": ""
  }
}
```

##### Status: 201 Recovery capability registered

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`expires_at` (required)**

    `string`, format: `date-time` — Deadline for starting recovery with the retained secret.

  - **`recovery_token_id` (required)**

    `string` — Identifier of the recovery authorization, not the recovery secret.

**Example:**

```json
{
  "data": {
    "recovery_token_id": "",
    "expires_at": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 Recovery intent conflicts with current device state

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Recovery authorization is temporarily unavailable

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/connectors/devices

- **Method:** `PARAMETERS`
- **Path:** `/v1/connectors/devices`

### List Connector devices

- **Method:** `GET`
- **Path:** `/v1/connectors/devices`
- **Tags:** Connectors

Lists assigned devices, including devices with revoked access. Requires read and agent scopes; ordinary account API keys additionally require keys scope. Device credentials cannot use this operation. Counts are current observations and pagination is not a snapshot.

#### Responses

##### Status: 200 Connector device page

###### Content-Type: application/json

- **`data` (required)**

  `array`

  **Items:**

  - **`agent_id` (required)**

    `string`

  - **`created_at` (required)**

    `string`, format: `date-time`

  - **`credential_status` (required)**

    `string`, possible values: `"active", "revoked", "registering"`

  - **`display_name` (required)**

    `string`

  - **`device_api_key_id`**

    `string` — Current device credential identifier, when registration has completed.

  - **`hostname`**

    `string`

  - **`last_active_at`**

    `string`, format: `date-time` — Most recent recorded native device activity; omitted when unavailable. This is not an online indicator.

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

- **`usage` (required)**

  `object`

  - **`active_device_credentials` (required)**

    `integer`, format: `int64`

  - **`device_count` (required)**

    `integer`, format: `int64` — Assigned device slots, including devices whose access was revoked.

  - **`device_credential_limit` (required)**

    `integer`

  - **`device_limit` (required)**

    `integer`

**Example:**

```json
{
  "data": [
    {
      "agent_id": "",
      "display_name": "",
      "hostname": "",
      "created_at": "",
      "last_active_at": "",
      "credential_status": "active",
      "device_api_key_id": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  },
  "usage": {
    "device_count": 1,
    "device_limit": 1,
    "active_device_credentials": 1,
    "device_credential_limit": 1
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Device information is temporarily unavailable

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/connectors/devices/{agent\_id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/connectors/devices/{agent_id}`

### Rename a Connector device

- **Method:** `PATCH`
- **Path:** `/v1/connectors/devices/{agent_id}`
- **Tags:** Connectors

Changes only the device display label. Requires write and agent scopes; ordinary account API keys additionally require keys scope. Device credentials cannot use this operation.

#### Request Body

##### Content-Type: application/json

- **`display_name` (required)**

  `string` — A device label without leading or trailing whitespace or control characters. It survives credential recovery.

**Example:**

```json
{
  "display_name": ""
}
```

#### Responses

##### Status: 200 Renamed device

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`agent_id` (required)**

    `string`

  - **`created_at` (required)**

    `string`, format: `date-time`

  - **`credential_status` (required)**

    `string`, possible values: `"active", "revoked", "registering"`

  - **`display_name` (required)**

    `string`

  - **`device_api_key_id`**

    `string` — Current device credential identifier, when registration has completed.

  - **`hostname`**

    `string`

  - **`last_active_at`**

    `string`, format: `date-time` — Most recent recorded native device activity; omitted when unavailable. This is not an online indicator.

**Example:**

```json
{
  "data": {
    "agent_id": "",
    "display_name": "",
    "hostname": "",
    "created_at": "",
    "last_active_at": "",
    "credential_status": "active",
    "device_api_key_id": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Device information is temporarily unavailable

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/connectors/agents/{agent\_id}/assignment

- **Method:** `PARAMETERS`
- **Path:** `/v1/connectors/agents/{agent_id}/assignment`

### Retire a Connector assignment

- **Method:** `DELETE`
- **Path:** `/v1/connectors/agents/{agent_id}/assignment`
- **Tags:** Connectors

Completes removal of one qURL Connector device after its device credential has been revoked. The operation is idempotent and requires JWT authentication or an ordinary account API key with both `qurl:keys` and `qurl:agent`.

#### Responses

##### Status: 204 Connector assignment is retired or was already absent

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The Connector device is still active or recovering

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Connector assignment retirement is temporarily unavailable

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}`

### Get resource details

- **Method:** `GET`
- **Path:** `/v1/resources/{id}`
- **Tags:** Resources

Returns resource details and a preview of up to 100 qURLs. Use its CRID as `id`. Each qURL has its own label, expiration, access rules, and session limits. Use `GET /v1/resources/{id}/qurls` to retrieve all links with pagination.

#### Responses

##### Status: 200 Resource details

###### Content-Type: application/json

- **`data`**

  `object`

  - **`qurls`**

    `array` — Unordered bounded preview of per-qURL summaries for this resource (maximum 100). This preview does not imply newest first or any stable ordering and is not active-first; an active qURL can be absent from the preview when earlier qURLs fill the cap. When present, \`resource.qurl\_count\` is the exact total; if omitted, callers must treat truncation as unknown. For the complete per-resource qURL list, use \`GET /v1/resources/{id}/qurls\`, which pages through every qURL with cursor-based pagination. For large resources, the preview and count are separate reads, so concurrent qURL changes can make the preview length and \`resource.qurl\_count\` briefly differ.

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

      - **`ai_agent_policy`**

        `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

        - **`allow_categories`**

          `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

        - **`block_all`**

          `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

        - **`deny_categories`**

          `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`geo_allowlist`**

        `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`geo_denylist`**

        `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`ip_allowlist`**

        `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`ip_denylist`**

        `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`user_agent_allow_regex`**

        `string` — Regular expression to allow matching user agents (max 256 characters)

      - **`user_agent_deny_regex`**

        `string` — Regular expression to deny matching user agents (max 256 characters)

    - **`created_at`**

      `string`, format: `date-time`

    - **`expires_at`**

      `string`, format: `date-time`

    - **`label`**

      `string`

    - **`max_sessions`**

      `integer`

    - **`one_time_use`**

      `boolean`

    - **`qurl_id`**

      `string`

    - **`qurl_site`**

      `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

    - **`session_duration`**

      `integer` — Session lifetime in seconds (0 = server default)

    - **`status`**

      `string`, possible values: `"active", "consumed", "expired", "revoked"`

    - **`target_path`**

      `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

    - **`use_count`**

      `integer`

  - **`resource`**

    `object`

    - **`access_requests`**

      `boolean` — Whether someone who opens this private resource's link without access can ask you for it. Always present. Change it with \`PATCH /v1/resources/{id}\`.

    - **`alias`**

      `string | null` — Per-owner human-readable handle for the resource (e.g. \`dev-dashboard\`). \`null\` when no alias is set. Lookup via \`GET /v1/resources?alias={alias}\` (returns a 0- or 1-item list).

    - **`allowed_device_keys`**

      `array` — Public X25519 keys of the devices that can create links for this private resource, in canonical padded standard base64. Omitted when no devices are listed; only the owner can then create links for a private resource. The list has no effect unless the resource is private.

      **Items:**

      `string`

    - **`allowed_passkeys`**

      `array` — The passkeys you approved for this private resource, oldest approval first. Always present; empty when you approved none. A passkey is added by approving an access request and removed with \`DELETE /v1/resources/{id}/allowed-passkeys/{device\_id}\`.

      **Items:**

      - **`approved_at` (required)**

        `string`, format: `date-time` — When you approved the request.

      - **`device_id` (required)**

        `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

      - **`name` (required)**

        `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

    - **`connector_routing_id`**

      `string` — Opaque routing label the qURL Connector must use verbatim; do not derive it client-side.

    - **`consumer_access`**

      `object`

      - **`mode` (required)**

        `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

      - **`version` (required)**

        `integer`, format: `int64`

      - **`contract_id`**

        `string` — Required only for contract mode. Names the resource-specific contract.

    - **`consumer_access_version`**

      `integer`, format: `int64` — Monotonic consumer policy revision. Zero means no consumer policy has been set.

    - **`created_at`**

      `string`, format: `date-time`

    - **`crid`**

      `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

    - **`custom_domain`**

      `string | null`

    - **`description`**

      `string`

    - **`desired_state`**

      `string`, possible values: `"on", "off"`

    - **`expires_at`**

      `string`, format: `date-time`

    - **`knock_resource_id`**

      `string` — Internal use.

    - **`preserve_host`**

      `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

    - **`private`**

      `boolean`, default: `false` — When true, only the owner and the devices listed in \`allowed\_device\_keys\` can create links for this resource. A resource that is not private is public. Privacy is chosen when the resource is created (a new resource is private unless its creator asked for a public one) and cannot change afterward. Older resources can omit this field; they are public.

    - **`publisher`**

      `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

      - **`verified` (required)**

        `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

      - **`name`**

        `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

    - **`qurl_count`**

      `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

    - **`resource_id`**

      `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

    - **`serving_epoch`**

      `integer`, format: `int64` — Monotonic qURL Connector serving lifecycle. Zero means sharing has never been started.

    - **`session_duration_cap`**

      `integer` — Maximum session duration, in seconds, for qURLs on this resource. It is fixed when \`POST /v1/qurls\` first creates the resource. Later link creation and updates cannot exceed it (\`400 invalid\_session\_duration\`). Omitted session durations inherit the cap. Absent means there is no resource cap; the 1-second to 24-hour field limits still apply. \`POST /v1/resources\` creates a resource without a cap, and later link creation does not add one. To change a cap, register a different target URL, or revoke this resource and recreate it through \`POST /v1/qurls\` with the required \`session\_duration\`. Revocation blocks all existing links. A new resource receives a new CRID.

    - **`slug`**

      `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. Distinct from \`alias\`: slugs are immutable post-create, aliases are mutable. Lookup via \`GET /v1/resources?slug={slug}\` returns a 0- or 1-item active-resource list. See \`CreateResourceRequest.slug\` for format, uniqueness, and \`find\_or\_create\` semantics.

    - **`status`**

      `string`, possible values: `"active", "revoked"`

    - **`tags`**

      `array`

      **Items:**

      `string`

    - **`target_url`**

      `string` — The URL to protect. Optional — omitted (field absent from the JSON object, NOT serialized as null) when not applicable to this resource, or when the resource is owned by a connector.

    - **`tombstoned_at`**

      `string`, format: `date-time` — When the resource's lifecycle closed. Present only on tombstoned resources; absent on active ones. Consumers fetching a single resource by ID handle the tombstoned-row case via the 410 response on \`GET /v1/resources/{id}\` — the same timestamp surfaces in \`meta.tombstone.tombstoned\_at\`. List / alias-lookup (\`GET /v1/resources?alias=...\`) responses include this field on the resource itself so callers can distinguish tombstoned from active without a per-row probe.

    - **`type`**

      `string` — Resource type (see \`ResourceType\` for the full enum). Management-API read responses on connector-owned upload rows redact user-content resource fields (\`target\_url\`, \`description\`, \`tags\`, \`custom\_domain\`, \`alias\`, \`preserve\_host\`, \`qurl\_count\`, \`slug\`) and nested qURL summary fields (\`label\`, \`target\_path\`). List/detail responses include the row but omit those fields.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "resource": {
      "consumer_access_version": 1,
      "consumer_access": {
        "mode": "keys",
        "version": 1,
        "contract_id": ""
      },
      "private": false,
      "allowed_device_keys": [
        ""
      ],
      "access_requests": true,
      "allowed_passkeys": [
        {
          "device_id": "k3wp-x2aq-7mzn-4tby",
          "name": "",
          "approved_at": ""
        }
      ],
      "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
      "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
      "type": "",
      "target_url": "",
      "knock_resource_id": "",
      "connector_routing_id": "",
      "status": "active",
      "desired_state": "on",
      "serving_epoch": 0,
      "description": "",
      "tags": [
        ""
      ],
      "custom_domain": null,
      "alias": null,
      "slug": "",
      "preserve_host": false,
      "session_duration_cap": 30,
      "qurl_count": 1,
      "created_at": "",
      "expires_at": "",
      "publisher": {
        "name": "Acme Docs",
        "verified": false
      },
      "tombstoned_at": ""
    },
    "qurls": [
      {
        "qurl_id": "",
        "label": "",
        "status": "active",
        "one_time_use": true,
        "max_sessions": 1,
        "session_duration": 1,
        "use_count": 1,
        "qurl_site": "",
        "target_path": "",
        "created_at": "",
        "expires_at": "",
        "access_policy": {
          "ip_allowlist": [
            "192.168.1.0/24",
            "10.0.0.1"
          ],
          "ip_denylist": [
            ""
          ],
          "geo_allowlist": [
            "US",
            "CA",
            "GB"
          ],
          "geo_denylist": [
            "CN",
            "RU"
          ],
          "user_agent_allow_regex": "^Mozilla.*",
          "user_agent_deny_regex": ".*bot.*",
          "ai_agent_policy": {
            "block_all": false,
            "deny_categories": [
              "gptbot",
              "commoncrawl",
              "bytedance"
            ],
            "allow_categories": [
              "claude",
              "chatgpt",
              "perplexity"
            ]
          }
        }
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 410 The resource is closed. You can no longer create links for it. The response includes retained resource metadata in \`meta.tombstone\`. This metadata remains available until the retention period ends.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Update resource metadata

- **Method:** `PATCH`
- **Path:** `/v1/resources/{id}`
- **Tags:** Resources

Updates resource metadata. Use its CRID as `id`. Omit `alias` to keep it, send a string to set it, or send `null` to clear it. An alias already used by another of your resources returns `409 alias_in_use`. If any change fails, none of the changes in the request are applied. One case is different: a request that sets a new `custom_domain` and also changes `tags`, `description` or `preserve_host`. The custom domain is saved first. If the other changes then fail, the request returns an error and the custom domain stays set.

Change `custom_domain` and `alias` in separate PATCH requests. Combining them returns `400 mutually_exclusive_fields`. Change `custom_domain` and `allowed_device_keys` in separate PATCH requests. Combining them returns `400 invalid_input`. The same holds for `allowed_device_keys_add` and `allowed_device_keys_remove`.

To change which devices can create links for a private resource, either replace the complete list with `allowed_device_keys`, or change single keys with `allowed_device_keys_add` and `allowed_device_keys_remove`. The two forms cannot be combined in one request (`400 invalid_input`). Adding and removing single keys is safe next to other requests that do the same: each request changes only the keys it names. Adding a key that is already listed, and removing a key that is not listed, change nothing and are not errors. The response always carries the complete resulting list.

A `503` means that nothing was changed and that the request can be sent again after the time in `Retry-After`. A request with `allowed_device_keys_add` or `allowed_device_keys_remove` gets it when other activity on the same resource, of any kind, kept its change from being saved.

A resource that is not active, for example a revoked one, cannot be updated: the request returns `400` with the code `invalid_input` or `revoked`, and changes nothing. Read `error.code`. The text of `error.detail` is for people and can change.

#### Request Body

##### Content-Type: application/json

- **`access_requests`**

  `boolean` — Turn access requests on or off. When on, someone who opens this private resource's link without access can ask you for it, and you approve or deny each request. Only a private resource can turn it on: \`true\` for a public resource returns \`400 invalid\_input\`. Turning it off stops new requests, and people without access are no longer told that the resource exists. It removes nothing: requests that are already pending stay in the list until you decide them or they expire, you can still approve them, and passkeys you approved keep their access. Update separately from \`custom\_domain\`; combining them returns \`400 invalid\_input\`.

- **`alias`**

  `string | null` — Set, rebind, or clear the resource's alias under RFC 7396 JSON Merge Patch semantics: - omitted → no change. - \`null\` → clear the alias and make the alias available again. Equivalent to deleting the alias. - string value → set or rebind. Pattern, length, and reserved-word rules match \`CreateResourceRequest.alias\`. Sending an empty string (\`""\`) is rejected with \`400 alias\_invalid\_format\` to avoid the silent-no-op trap on callers building this body from user input — use \`null\` for the clear directive.

- **`allowed_device_keys`**

  `array` — Replace the complete list of public X25519 keys of the devices that can create links for this private resource. Use canonical padded standard base64. Only the publisher can change this list; with an empty list only the owner can create links. The list has no effect unless the resource is private. Update separately from custom\_domain; combining them returns 400 invalid\_input. Cannot be combined with \`allowed\_device\_keys\_add\` or \`allowed\_device\_keys\_remove\`. To change single keys without sending the whole list, use those two fields instead.

  **Items:**

  `string`

- **`allowed_device_keys_add`**

  `array` — Public X25519 keys to add to the devices that can create links for this private resource, in canonical padded standard base64. The other listed keys stay as they are. A key that is already listed changes nothing. The resulting list can hold at most 256 keys, counted after the removals of the same request; a request that would exceed that returns \`400 invalid\_input\` and changes nothing. Can be sent together with \`allowed\_device\_keys\_remove\`; a key in both returns \`400 invalid\_input\`. Cannot be combined with \`allowed\_device\_keys\` or \`custom\_domain\` (\`400 invalid\_input\`).

  **Items:**

  `string`

- **`allowed_device_keys_remove`**

  `array` — Public X25519 keys to remove from the devices that can create links for this private resource, in canonical padded standard base64. The other listed keys stay as they are. A key that is not listed changes nothing. Removing a key blocks new link requests from that device; links it already created keep working until they expire or are revoked. Can be sent together with \`allowed\_device\_keys\_add\`; a key in both returns \`400 invalid\_input\`. Cannot be combined with \`allowed\_device\_keys\` or \`custom\_domain\` (\`400 invalid\_input\`).

  **Items:**

  `string`

- **`consumer_access`**

  `object`

  - **`mode` (required)**

    `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`contract_id`**

    `string` — Required only for contract mode. Names the resource-specific contract.

- **`custom_domain`**

  `string`

- **`description`**

  `string`

- **`expected_consumer_access_version`**

  `integer`, format: `int64` — Required when consumer\_access is changed. Zero means no prior consumer policy.

- **`preserve_host`**

  `boolean` — Whether to preserve the original Host header when proxying to the origin via a custom domain. When true, the custom domain is sent as the Host header. When false (default), the target server's hostname is used. Only meaningful when custom\_domain is set.

- **`tags`**

  `array`

  **Items:**

  `string`

**Example:**

```json
{
  "consumer_access": {
    "mode": "keys",
    "version": 1,
    "contract_id": ""
  },
  "expected_consumer_access_version": 0,
  "access_requests": true,
  "allowed_device_keys": [
    ""
  ],
  "allowed_device_keys_add": [
    ""
  ],
  "allowed_device_keys_remove": [
    ""
  ],
  "description": "",
  "tags": [
    ""
  ],
  "custom_domain": "",
  "preserve_host": true,
  "alias": null
}
```

#### Responses

##### Status: 200 Resource updated

###### Content-Type: application/json

- **`data`**

  `object`

  - **`access_requests`**

    `boolean` — Whether someone who opens this private resource's link without access can ask you for it. Always present. Change it with \`PATCH /v1/resources/{id}\`.

  - **`alias`**

    `string | null` — Per-owner human-readable handle for the resource (e.g. \`dev-dashboard\`). \`null\` when no alias is set. Lookup via \`GET /v1/resources?alias={alias}\` (returns a 0- or 1-item list).

  - **`allowed_device_keys`**

    `array` — Public X25519 keys of the devices that can create links for this private resource, in canonical padded standard base64. Omitted when no devices are listed; only the owner can then create links for a private resource. The list has no effect unless the resource is private.

    **Items:**

    `string`

  - **`allowed_passkeys`**

    `array` — The passkeys you approved for this private resource, oldest approval first. Always present; empty when you approved none. A passkey is added by approving an access request and removed with \`DELETE /v1/resources/{id}/allowed-passkeys/{device\_id}\`.

    **Items:**

    - **`approved_at` (required)**

      `string`, format: `date-time` — When you approved the request.

    - **`device_id` (required)**

      `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

    - **`name` (required)**

      `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

  - **`connector_routing_id`**

    `string` — Opaque routing label the qURL Connector must use verbatim; do not derive it client-side.

  - **`consumer_access`**

    `object`

    - **`mode` (required)**

      `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

    - **`version` (required)**

      `integer`, format: `int64`

    - **`contract_id`**

      `string` — Required only for contract mode. Names the resource-specific contract.

  - **`consumer_access_version`**

    `integer`, format: `int64` — Monotonic consumer policy revision. Zero means no consumer policy has been set.

  - **`created_at`**

    `string`, format: `date-time`

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`desired_state`**

    `string`, possible values: `"on", "off"`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`knock_resource_id`**

    `string` — Internal use.

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`private`**

    `boolean`, default: `false` — When true, only the owner and the devices listed in \`allowed\_device\_keys\` can create links for this resource. A resource that is not private is public. Privacy is chosen when the resource is created (a new resource is private unless its creator asked for a public one) and cannot change afterward. Older resources can omit this field; they are public.

  - **`publisher`**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`qurl_count`**

    `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

  - **`serving_epoch`**

    `integer`, format: `int64` — Monotonic qURL Connector serving lifecycle. Zero means sharing has never been started.

  - **`session_duration_cap`**

    `integer` — Maximum session duration, in seconds, for qURLs on this resource. It is fixed when \`POST /v1/qurls\` first creates the resource. Later link creation and updates cannot exceed it (\`400 invalid\_session\_duration\`). Omitted session durations inherit the cap. Absent means there is no resource cap; the 1-second to 24-hour field limits still apply. \`POST /v1/resources\` creates a resource without a cap, and later link creation does not add one. To change a cap, register a different target URL, or revoke this resource and recreate it through \`POST /v1/qurls\` with the required \`session\_duration\`. Revocation blocks all existing links. A new resource receives a new CRID.

  - **`slug`**

    `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. Distinct from \`alias\`: slugs are immutable post-create, aliases are mutable. Lookup via \`GET /v1/resources?slug={slug}\` returns a 0- or 1-item active-resource list. See \`CreateResourceRequest.slug\` for format, uniqueness, and \`find\_or\_create\` semantics.

  - **`status`**

    `string`, possible values: `"active", "revoked"`

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `string` — The URL to protect. Optional — omitted (field absent from the JSON object, NOT serialized as null) when not applicable to this resource, or when the resource is owned by a connector.

  - **`tombstoned_at`**

    `string`, format: `date-time` — When the resource's lifecycle closed. Present only on tombstoned resources; absent on active ones. Consumers fetching a single resource by ID handle the tombstoned-row case via the 410 response on \`GET /v1/resources/{id}\` — the same timestamp surfaces in \`meta.tombstone.tombstoned\_at\`. List / alias-lookup (\`GET /v1/resources?alias=...\`) responses include this field on the resource itself so callers can distinguish tombstoned from active without a per-row probe.

  - **`type`**

    `string` — Resource type (see \`ResourceType\` for the full enum). Management-API read responses on connector-owned upload rows redact user-content resource fields (\`target\_url\`, \`description\`, \`tags\`, \`custom\_domain\`, \`alias\`, \`preserve\_host\`, \`qurl\_count\`, \`slug\`) and nested qURL summary fields (\`label\`, \`target\_path\`). List/detail responses include the row but omit those fields.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "consumer_access_version": 1,
    "consumer_access": {
      "mode": "keys",
      "version": 1,
      "contract_id": ""
    },
    "private": false,
    "allowed_device_keys": [
      ""
    ],
    "access_requests": true,
    "allowed_passkeys": [
      {
        "device_id": "k3wp-x2aq-7mzn-4tby",
        "name": "",
        "approved_at": ""
      }
    ],
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "type": "",
    "target_url": "",
    "knock_resource_id": "",
    "connector_routing_id": "",
    "status": "active",
    "desired_state": "on",
    "serving_epoch": 0,
    "description": "",
    "tags": [
      ""
    ],
    "custom_domain": null,
    "alias": null,
    "slug": "",
    "preserve_host": false,
    "session_duration_cap": 30,
    "qurl_count": 1,
    "created_at": "",
    "expires_at": "",
    "publisher": {
      "name": "Acme Docs",
      "verified": false
    },
    "tombstoned_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 Alias already in use by another resource. The entire PATCH is rolled back — all fields sent in the same request are NOT persisted.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 410 The resource is closed and this operation is no longer available. Use an active resource. The response can include \`meta.tombstone\` with retained resource metadata; clients must also handle its absence.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Revoke resource and all its qURLs

- **Method:** `DELETE`
- **Path:** `/v1/resources/{id}`
- **Tags:** Resources

Revokes a resource and blocks access through all its qURLs. Use the resource's CRID. To revoke one link, use `DELETE /v1/resources/{id}/qurls/{qurl_id}` instead. Repeating this request does not emit another revoke webhook.

On `503`, revocation is saved but enforcement is still pending. Wait for `Retry-After`, then retry the same DELETE to confirm enforcement.

#### Responses

##### Status: 204 Resource revoked

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Read a CRID card preview

- **Method:** `GET`
- **Path:** `/v1/crid-previews/{token}`
- **Tags:** Resources

Read display metadata with the short-lived opaque token issued by a successful CRID decision. This token grants no access to resource content.

#### Responses

##### Status: 200 Display metadata

###### Content-Type: application/json

- **`description`**

  `string`

- **`media`**

  `array`

  **Items:**

  - **`alt`**

    `string`

  - **`clip`**

    `string`, format: `uri`

  - **`cover`**

    `string`, format: `uri`

  - **`fallback`**

    `string`, format: `uri`

  - **`height`**

    `integer`

  - **`id`**

    `string`

  - **`image`**

    `string`, format: `uri`

  - **`kind`**

    `string`, possible values: `"image", "video"`

  - **`width`**

    `integer`

- **`name`**

  `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

**Example:**

```json
{
  "name": "",
  "description": "",
  "media": [
    {
      "id": "",
      "kind": "image",
      "alt": "",
      "width": 1,
      "height": 1,
      "cover": "",
      "fallback": "",
      "image": "",
      "clip": ""
    }
  ]
}
```

##### Status: default Preview is unavailable

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/preview

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/preview`

### Read one CRID preview

- **Method:** `GET`
- **Path:** `/v1/resources/{id}/preview`
- **Tags:** Resources

Read the latest name, description, media IDs, processing state and optional signed card for an owned CRID.

#### Responses

##### Status: 200 Current preview metadata

###### Content-Type: application/json

- **`card`**

  `object`

  - **`description`**

    `string`

  - **`media`**

    `array`

    **Items:**

    - **`alt`**

      `string`

    - **`clip`**

      `string`, format: `uri`

    - **`cover`**

      `string`, format: `uri`

    - **`fallback`**

      `string`, format: `uri`

    - **`height`**

      `integer`

    - **`id`**

      `string`

    - **`image`**

      `string`, format: `uri`

    - **`kind`**

      `string`, possible values: `"image", "video"`

    - **`width`**

      `integer`

  - **`name`**

    `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

- **`description`**

  `string`

- **`media`**

  `array`

  **Items:**

  - **`alt`**

    `string`

  - **`clip`**

    `string`, format: `uri`

  - **`cover`**

    `string`, format: `uri`

  - **`fallback`**

    `string`, format: `uri`

  - **`height`**

    `integer`

  - **`id`**

    `string`

  - **`image`**

    `string`, format: `uri`

  - **`kind`**

    `string`, possible values: `"image", "video"`

  - **`width`**

    `integer`

- **`name`**

  `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

- **`revision`**

  `integer`, format: `int64`

- **`uploads`**

  `array`

  **Items:**

  - **`alt`**

    `string`

  - **`content_type`**

    `string`

  - **`expires_at`**

    `integer`, format: `int64`

  - **`id`**

    `string`

  - **`replace_id`**

    `string`

  - **`size`**

    `integer`, format: `int64`

  - **`status`**

    `string`, possible values: `"processing", "failed"`

**Example:**

```json
{
  "revision": 0,
  "name": "",
  "description": "",
  "media": [
    {
      "id": "",
      "kind": "image",
      "alt": "",
      "width": 1,
      "height": 1,
      "cover": "",
      "fallback": "",
      "image": "",
      "clip": ""
    }
  ],
  "card": {
    "name": "",
    "description": "",
    "media": [
      {
        "id": "",
        "kind": "image",
        "alt": "",
        "width": 1,
        "height": 1,
        "cover": "",
        "fallback": "",
        "image": "",
        "clip": ""
      }
    ]
  },
  "uploads": [
    {
      "id": "",
      "replace_id": "",
      "alt": "",
      "content_type": "",
      "size": 1,
      "expires_at": 1,
      "status": "processing"
    }
  ]
}
```

##### Status: default The request could not be completed

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Edit one CRID preview

- **Method:** `PATCH`
- **Path:** `/v1/resources/{id}/preview`
- **Tags:** Resources

Edit the public name, description or media order using the latest revision. An empty media list removes media and cancels uploads.

#### Request Body

##### Content-Type: application/json

- **`revision` (required)**

  `integer`, format: `int64`

- **`description`**

  `string`

- **`media_ids`**

  `array`

  **Items:**

  `string`

- **`name`**

  `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

**Example:**

```json
{
  "revision": 0,
  "name": "",
  "description": "",
  "media_ids": [
    ""
  ]
}
```

#### Responses

##### Status: 200 Updated preview metadata

###### Content-Type: application/json

- **`card`**

  `object`

  - **`description`**

    `string`

  - **`media`**

    `array`

    **Items:**

    - **`alt`**

      `string`

    - **`clip`**

      `string`, format: `uri`

    - **`cover`**

      `string`, format: `uri`

    - **`fallback`**

      `string`, format: `uri`

    - **`height`**

      `integer`

    - **`id`**

      `string`

    - **`image`**

      `string`, format: `uri`

    - **`kind`**

      `string`, possible values: `"image", "video"`

    - **`width`**

      `integer`

  - **`name`**

    `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

- **`description`**

  `string`

- **`media`**

  `array`

  **Items:**

  - **`alt`**

    `string`

  - **`clip`**

    `string`, format: `uri`

  - **`cover`**

    `string`, format: `uri`

  - **`fallback`**

    `string`, format: `uri`

  - **`height`**

    `integer`

  - **`id`**

    `string`

  - **`image`**

    `string`, format: `uri`

  - **`kind`**

    `string`, possible values: `"image", "video"`

  - **`width`**

    `integer`

- **`name`**

  `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

- **`revision`**

  `integer`, format: `int64`

- **`uploads`**

  `array`

  **Items:**

  - **`alt`**

    `string`

  - **`content_type`**

    `string`

  - **`expires_at`**

    `integer`, format: `int64`

  - **`id`**

    `string`

  - **`replace_id`**

    `string`

  - **`size`**

    `integer`, format: `int64`

  - **`status`**

    `string`, possible values: `"processing", "failed"`

**Example:**

```json
{
  "revision": 0,
  "name": "",
  "description": "",
  "media": [
    {
      "id": "",
      "kind": "image",
      "alt": "",
      "width": 1,
      "height": 1,
      "cover": "",
      "fallback": "",
      "image": "",
      "clip": ""
    }
  ],
  "card": {
    "name": "",
    "description": "",
    "media": [
      {
        "id": "",
        "kind": "image",
        "alt": "",
        "width": 1,
        "height": 1,
        "cover": "",
        "fallback": "",
        "image": "",
        "clip": ""
      }
    ]
  },
  "uploads": [
    {
      "id": "",
      "replace_id": "",
      "alt": "",
      "content_type": "",
      "size": 1,
      "expires_at": 1,
      "status": "processing"
    }
  ]
}
```

##### Status: default The request could not be completed

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/preview/uploads

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/preview/uploads`

### Upload media for a verified publisher

- **Method:** `POST`
- **Path:** `/v1/resources/{id}/preview/uploads`
- **Tags:** Resources

Reserve an exact-size file upload for a currently verified publisher. Send bytes directly to the returned private storage PUT URL.

#### Request Body

##### Content-Type: application/json

- **`alt` (required)**

  `string`

- **`content_type` (required)**

  `string`, possible values: `"image/jpeg", "image/png", "image/webp", "image/gif", "video/mp4", "video/webm"`

- **`revision` (required)**

  `integer`, format: `int64`

- **`size` (required)**

  `integer`, format: `int64`

- **`replace_id`**

  `string`

**Example:**

```json
{
  "revision": 0,
  "content_type": "image/jpeg",
  "size": 1,
  "alt": "",
  "replace_id": ""
}
```

#### Responses

##### Status: 200 Single-write upload URL, required headers and updated preview revision

###### Content-Type: application/json

- **`headers` (required)**

  `object`

- **`preview` (required)**

  `object`

  - **`card`**

    `object`

    - **`description`**

      `string`

    - **`media`**

      `array`

      **Items:**

      - **`alt`**

        `string`

      - **`clip`**

        `string`, format: `uri`

      - **`cover`**

        `string`, format: `uri`

      - **`fallback`**

        `string`, format: `uri`

      - **`height`**

        `integer`

      - **`id`**

        `string`

      - **`image`**

        `string`, format: `uri`

      - **`kind`**

        `string`, possible values: `"image", "video"`

      - **`width`**

        `integer`

    - **`name`**

      `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

  - **`description`**

    `string`

  - **`media`**

    `array`

    **Items:**

    - **`alt`**

      `string`

    - **`clip`**

      `string`, format: `uri`

    - **`cover`**

      `string`, format: `uri`

    - **`fallback`**

      `string`, format: `uri`

    - **`height`**

      `integer`

    - **`id`**

      `string`

    - **`image`**

      `string`, format: `uri`

    - **`kind`**

      `string`, possible values: `"image", "video"`

    - **`width`**

      `integer`

  - **`name`**

    `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

  - **`revision`**

    `integer`, format: `int64`

  - **`uploads`**

    `array`

    **Items:**

    - **`alt`**

      `string`

    - **`content_type`**

      `string`

    - **`expires_at`**

      `integer`, format: `int64`

    - **`id`**

      `string`

    - **`replace_id`**

      `string`

    - **`size`**

      `integer`, format: `int64`

    - **`status`**

      `string`, possible values: `"processing", "failed"`

- **`upload_id` (required)**

  `string`

- **`url` (required)**

  `string`, format: `uri`

**Example:**

```json
{
  "preview": {
    "revision": 0,
    "name": "",
    "description": "",
    "media": [
      {
        "id": "",
        "kind": "image",
        "alt": "",
        "width": 1,
        "height": 1,
        "cover": "",
        "fallback": "",
        "image": "",
        "clip": ""
      }
    ],
    "card": {
      "name": "",
      "description": "",
      "media": [
        {
          "id": "",
          "kind": "image",
          "alt": "",
          "width": 1,
          "height": 1,
          "cover": "",
          "fallback": "",
          "image": "",
          "clip": ""
        }
      ]
    },
    "uploads": [
      {
        "id": "",
        "replace_id": "",
        "alt": "",
        "content_type": "",
        "size": 1,
        "expires_at": 1,
        "status": "processing"
      }
    ]
  },
  "upload_id": "",
  "url": "",
  "headers": {
    "additionalProperty": ""
  }
}
```

##### Status: default The request could not be completed

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/sharing

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/sharing`

### Get qURL Connector sharing status

- **Method:** `GET`
- **Path:** `/v1/resources/{id}/sharing`
- **Tags:** Resources

Returns durable start/stop intent and the current observed connection state for an owned qURL Connector resource. `serving` requires at least one authenticated Connector connection that is ready to carry traffic for the current lifecycle.

The response also carries the resource's `created_at` and your `publisher` profile, as people who request a link will see them. `publisher.verified: false` means the publisher name is self-declared and has not been confirmed.

#### Responses

##### Status: 200 Connector sharing status

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`connection_state` (required)**

    `string`, possible values: `"stopped", "connecting", "serving"` — GET derives this from authenticated Connector connections for the current lifecycle. A lifecycle mutation that rotates \`serving\_epoch\` reports \`connecting\` conservatively until a later GET observes a ready connection.

  - **`created_at` (required)**

    `string`, format: `date-time` — When this Connector resource was created.

  - **`crid` (required)**

    `string` — Required permanent, location-independent identifier for this Connector resource. Stable for the life of the resource. A CRID-addressed request returns that CRID; a resource-key request returns the canonical CRID.

  - **`desired_state` (required)**

    `string`, possible values: `"on", "off"`

  - **`publisher` (required)**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`resource_id` (required)**

    `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

  - **`serving_epoch` (required)**

    `integer`, format: `int64`

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "desired_state": "on",
    "serving_epoch": 0,
    "connection_state": "stopped",
    "created_at": "2026-03-01T09:00:00Z",
    "publisher": {
      "name": "Acme Docs",
      "verified": false
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 410 The resource or access token is no longer available; check error.code

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Start or stop qURL Connector sharing

- **Method:** `PUT`
- **Path:** `/v1/resources/{id}/sharing`
- **Tags:** Resources

Sets durable desired sharing state. Repeating the same state is idempotent and preserves `serving_epoch`; changing state rotates the epoch so connections from the previous lifecycle stop serving immediately. A response that rotates the epoch reports `connection_state=connecting` conservatively; use GET to observe the later transition to `serving`. An unchanged `on` request performs the same fail-closed observation as GET and can return 503 when the current connection state cannot be determined unambiguously.

#### Request Body

##### Content-Type: application/json

- **`desired_state` (required)**

  `string`, possible values: `"on", "off"`

**Example:**

```json
{
  "desired_state": "on"
}
```

#### Responses

##### Status: 200 Updated Connector sharing status

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`connection_state` (required)**

    `string`, possible values: `"stopped", "connecting", "serving"` — GET derives this from authenticated Connector connections for the current lifecycle. A lifecycle mutation that rotates \`serving\_epoch\` reports \`connecting\` conservatively until a later GET observes a ready connection.

  - **`created_at` (required)**

    `string`, format: `date-time` — When this Connector resource was created.

  - **`crid` (required)**

    `string` — Required permanent, location-independent identifier for this Connector resource. Stable for the life of the resource. A CRID-addressed request returns that CRID; a resource-key request returns the canonical CRID.

  - **`desired_state` (required)**

    `string`, possible values: `"on", "off"`

  - **`publisher` (required)**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`resource_id` (required)**

    `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

  - **`serving_epoch` (required)**

    `integer`, format: `int64`

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "desired_state": "on",
    "serving_epoch": 0,
    "connection_state": "stopped",
    "created_at": "2026-03-01T09:00:00Z",
    "publisher": {
      "name": "Acme Docs",
      "verified": false
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 410 The resource or access token is no longer available; check error.code

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/sharing/restart

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/sharing/restart`

### Restart qURL Connector sharing

- **Method:** `POST`
- **Path:** `/v1/resources/{id}/sharing/restart`
- **Tags:** Resources

Sets desired sharing state to `on` and always rotates `serving_epoch`, including when the Connector was stopped. Existing connections stop serving immediately and the Connector reconnects automatically. The mutation response reports `connection_state=connecting` conservatively; use GET to observe the later transition to `serving`.

#### Responses

##### Status: 200 Restarted Connector sharing status

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`connection_state` (required)**

    `string`, possible values: `"stopped", "connecting", "serving"` — GET derives this from authenticated Connector connections for the current lifecycle. A lifecycle mutation that rotates \`serving\_epoch\` reports \`connecting\` conservatively until a later GET observes a ready connection.

  - **`created_at` (required)**

    `string`, format: `date-time` — When this Connector resource was created.

  - **`crid` (required)**

    `string` — Required permanent, location-independent identifier for this Connector resource. Stable for the life of the resource. A CRID-addressed request returns that CRID; a resource-key request returns the canonical CRID.

  - **`desired_state` (required)**

    `string`, possible values: `"on", "off"`

  - **`publisher` (required)**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`resource_id` (required)**

    `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

  - **`serving_epoch` (required)**

    `integer`, format: `int64`

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "desired_state": "on",
    "serving_epoch": 0,
    "connection_state": "stopped",
    "created_at": "2026-03-01T09:00:00Z",
    "publisher": {
      "name": "Acme Docs",
      "verified": false
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 410 The resource or access token is no longer available; check error.code

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/invitations

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/invitations`

### inviteConsumer

- **Method:** `POST`
- **Path:** `/v1/resources/{id}/invitations`
- **Tags:** Resources

#### Request Body

##### Content-Type: application/json

- **`email` (required)**

  `string`, format: `email`

**Example:**

```json
{
  "email": ""
}
```

#### Responses

##### Status: 200 Current publisher-controlled consumer access decision

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`delivery` (required)**

    `string`, possible values: `"queued", "accepted", "failed"`

  - **`email` (required)**

    `string`

  - **`expires_at` (required)**

    `string`, format: `date-time`

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"open", "enrolled", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "session_cleanup": "pending",
    "id": "",
    "email": "",
    "status": "open",
    "delivery": "queued",
    "version": 1,
    "expires_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### listConsumerInvitations

- **Method:** `GET`
- **Path:** `/v1/resources/{id}/invitations`
- **Tags:** Resources

#### Responses

##### Status: 200 Current publisher-controlled consumer access decision

###### Content-Type: application/json

- **`data` (required)**

  `array`

  **Items:**

  - **`delivery` (required)**

    `string`, possible values: `"queued", "accepted", "failed"`

  - **`email` (required)**

    `string`

  - **`expires_at` (required)**

    `string`, format: `date-time`

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"open", "enrolled", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "session_cleanup": "pending",
      "id": "",
      "email": "",
      "status": "open",
      "delivery": "queued",
      "version": 1,
      "expires_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/invitations/{invitation\_id}/resend

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/invitations/{invitation_id}/resend`

### resendConsumerInvitation

- **Method:** `POST`
- **Path:** `/v1/resources/{id}/invitations/{invitation_id}/resend`
- **Tags:** Resources

#### Request Body

##### Content-Type: application/json

- **`expected_version` (required)**

  `integer`, format: `int64`

**Example:**

```json
{
  "expected_version": 1
}
```

#### Responses

##### Status: 200 Current publisher-controlled consumer access decision

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`delivery` (required)**

    `string`, possible values: `"queued", "accepted", "failed"`

  - **`email` (required)**

    `string`

  - **`expires_at` (required)**

    `string`, format: `date-time`

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"open", "enrolled", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "session_cleanup": "pending",
    "id": "",
    "email": "",
    "status": "open",
    "delivery": "queued",
    "version": 1,
    "expires_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/invitations/{invitation\_id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/invitations/{invitation_id}`

### revokeConsumerInvitation

- **Method:** `DELETE`
- **Path:** `/v1/resources/{id}/invitations/{invitation_id}`
- **Tags:** Resources

#### Responses

##### Status: 200 Current publisher-controlled consumer access decision

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`delivery` (required)**

    `string`, possible values: `"queued", "accepted", "failed"`

  - **`email` (required)**

    `string`

  - **`expires_at` (required)**

    `string`, format: `date-time`

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"open", "enrolled", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "session_cleanup": "pending",
    "id": "",
    "email": "",
    "status": "open",
    "delivery": "queued",
    "version": 1,
    "expires_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/consumer-grants

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/consumer-grants`

### listConsumerGrants

- **Method:** `GET`
- **Path:** `/v1/resources/{id}/consumer-grants`
- **Tags:** Resources

#### Responses

##### Status: 200 Current publisher-controlled consumer access decision

###### Content-Type: application/json

- **`data` (required)**

  `array`

  **Items:**

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"pending", "active", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`credential_id`**

    `string`

  - **`credential_kind`**

    `string`, possible values: `"passkey", "device", "email"`

  - **`credential_public_key_b64`**

    `string`

  - **`email`**

    `string`

  - **`email_verified_at`**

    `string`, format: `date-time`

  - **`entitlement_id`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "session_cleanup": "pending",
      "id": "",
      "status": "pending",
      "version": 1,
      "credential_kind": "passkey",
      "credential_id": "",
      "credential_public_key_b64": "",
      "email": "",
      "email_verified_at": "",
      "expires_at": "",
      "entitlement_id": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/consumer-grants/{grant\_id}/approve

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/consumer-grants/{grant_id}/approve`

### approveConsumerGrant

- **Method:** `POST`
- **Path:** `/v1/resources/{id}/consumer-grants/{grant_id}/approve`
- **Tags:** Resources

#### Request Body

##### Content-Type: application/json

- **`expected_version` (required)**

  `integer`, format: `int64`

- **`credential_id`**

  `string`

- **`credential_kind`**

  `string`, possible values: `"passkey", "device"`

- **`credential_public_key_b64`**

  `string`

- **`entitlement_id`**

  `string`

- **`expires_at`**

  `string`, format: `date-time`

**Example:**

```json
{
  "expected_version": 0,
  "credential_kind": "passkey",
  "credential_id": "",
  "credential_public_key_b64": "",
  "entitlement_id": "",
  "expires_at": ""
}
```

#### Responses

##### Status: 200 Current publisher-controlled consumer access decision

###### Content-Type: application/json

- **`data` (required)**

  `object` — Contract mode omits credential and contact identity. Email is returned only after mailbox proof and disclosure consent.

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"pending", "active", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`credential_id`**

    `string`

  - **`credential_kind`**

    `string`, possible values: `"passkey", "device", "email"`

  - **`credential_public_key_b64`**

    `string`

  - **`email`**

    `string`

  - **`email_verified_at`**

    `string`, format: `date-time`

  - **`entitlement_id`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "session_cleanup": "pending",
    "id": "",
    "status": "pending",
    "version": 1,
    "credential_kind": "passkey",
    "credential_id": "",
    "credential_public_key_b64": "",
    "email": "",
    "email_verified_at": "",
    "expires_at": "",
    "entitlement_id": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/consumer-grants/{grant\_id}/deny

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/consumer-grants/{grant_id}/deny`

### denyConsumerGrant

- **Method:** `POST`
- **Path:** `/v1/resources/{id}/consumer-grants/{grant_id}/deny`
- **Tags:** Resources

#### Request Body

##### Content-Type: application/json

- **`expected_version` (required)**

  `integer`, format: `int64`

**Example:**

```json
{
  "expected_version": 1
}
```

#### Responses

##### Status: 200 Current publisher-controlled consumer access decision

###### Content-Type: application/json

- **`data` (required)**

  `object` — Contract mode omits credential and contact identity. Email is returned only after mailbox proof and disclosure consent.

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"pending", "active", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`credential_id`**

    `string`

  - **`credential_kind`**

    `string`, possible values: `"passkey", "device", "email"`

  - **`credential_public_key_b64`**

    `string`

  - **`email`**

    `string`

  - **`email_verified_at`**

    `string`, format: `date-time`

  - **`entitlement_id`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "session_cleanup": "pending",
    "id": "",
    "status": "pending",
    "version": 1,
    "credential_kind": "passkey",
    "credential_id": "",
    "credential_public_key_b64": "",
    "email": "",
    "email_verified_at": "",
    "expires_at": "",
    "entitlement_id": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/consumer-grants/{grant\_id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/consumer-grants/{grant_id}`

### revokeConsumerGrant

- **Method:** `DELETE`
- **Path:** `/v1/resources/{id}/consumer-grants/{grant_id}`
- **Tags:** Resources

#### Responses

##### Status: 200 Current publisher-controlled consumer access decision

###### Content-Type: application/json

- **`data` (required)**

  `object` — Contract mode omits credential and contact identity. Email is returned only after mailbox proof and disclosure consent.

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"pending", "active", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`credential_id`**

    `string`

  - **`credential_kind`**

    `string`, possible values: `"passkey", "device", "email"`

  - **`credential_public_key_b64`**

    `string`

  - **`email`**

    `string`

  - **`email_verified_at`**

    `string`, format: `date-time`

  - **`entitlement_id`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "session_cleanup": "pending",
    "id": "",
    "status": "pending",
    "version": 1,
    "credential_kind": "passkey",
    "credential_id": "",
    "credential_public_key_b64": "",
    "email": "",
    "email_verified_at": "",
    "expires_at": "",
    "entitlement_id": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/access-requests

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/access-requests`

### List pending access requests for a resource

- **Method:** `GET`
- **Path:** `/v1/resources/{id}/access-requests`
- **Tags:** Resources

Lists the people who asked for access to this private resource and are waiting for your decision, oldest first. A resource holds at most 20 pending requests, so the list is never paged. A request waits for 7 days and is then dropped.

Someone who opens the resource's link without access can ask for it when the resource has `access_requests` on. They see a 6-digit code and give it to you. You approve with that code (`POST /v1/resources/{id}/access-requests/{request_code}/approve`).

This list does not return the codes, and no other response of this API returns a pending code. The code is what ties a request to a person: you learn it only from the person who asked. The `name` is what the requester typed and proves nothing, so a list that showed the codes would let anyone approve by name.

To refuse a request from this list, deny it by its `device_id` (`DELETE /v1/resources/{id}/access-requests/{device_id}`).

#### Responses

##### Status: 200 Pending access requests

###### Content-Type: application/json

- **`data` (required)**

  `array`

  **Items:**

  - **`device_id` (required)**

    `string` — Identifies the requester's passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. A request from a passkey you approved before, on another resource, has the \`device\_id\` you saw then. Deny the request by this value.

  - **`expires_at` (required)**

    `string`, format: `date-time` — When the request is dropped if you do not decide it.

  - **`name` (required)**

    `string` — The name the requester typed. It is not verified. Display it as untrusted text.

  - **`requested_at` (required)**

    `string`, format: `date-time`

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": [
    {
      "name": "",
      "device_id": "k3wp-x2aq-7mzn-4tby",
      "requested_at": "",
      "expires_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/access-requests/{device\_id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/access-requests/{device_id}`

### Deny an access request

- **Method:** `DELETE`
- **Path:** `/v1/resources/{id}/access-requests/{device_id}`
- **Tags:** Resources

Removes a pending access request. The requester is not told and can ask again.

Name the request by its `device_id`, as the list shows it. Every pending request of this resource from that device is removed. A request of the same device for another resource is not touched.

If the requester gave you their 6-digit code and you refuse them, you can send the code in place of the `device_id`. The two cannot be confused: a `device_id` has the form `xxxx-xxxx-xxxx-xxxx`, and a code is six digits.

A `device_id` or a code that names no pending request of this resource returns `404`: it was never issued, the request expired, or it was already approved or denied. A value that is neither a `device_id` nor a code returns the same `404`.

A code that names no pending request counts as a wrong code, in the same count as a wrong code on approve. After 5 wrong codes for this resource within one hour, a denial by code returns `429 rate_limited` until `Retry-After` has passed. A denial by `device_id` is never counted and never limited.

On `503`, nothing was changed. Wait for `Retry-After`, then send the request again.

#### Responses

##### Status: 204 Request denied

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Too many wrong codes were tried for this resource (\`rate\_limited\`). No code is accepted for it, a correct one included, until \`Retry-After\` has passed. The rate limit of your plan also answers \`429\`, with the code \`rate\_limit\_exceeded\`.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/access-requests/{request\_code}/approve

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/access-requests/{request_code}/approve`

### Approve an access request

- **Method:** `POST`
- **Path:** `/v1/resources/{id}/access-requests/{request_code}/approve`
- **Tags:** Resources

Approves a pending access request. The requester's passkey can then open this resource, and the request leaves the pending list. Both happen together or not at all. The approved passkey appears in the resource's `allowed_passkeys`.

You approve with the 6-digit code that the requester gives you, and with nothing else: a `device_id` does not approve. No response of this API returns the code of a pending request, so that the person you approve is the person who told you the code, and not a name in a list.

The request has no body. An empty JSON object is accepted too.

A code that names no pending request of this resource returns `404`: it was never issued, the request expired, or it was already approved or denied. A second approval of the same code therefore returns `404`; read the resource to see the result.

Wrong codes are limited, so that a code cannot be found by trying. A resource takes at most 5 wrong codes within one hour, counted over approvals and denials by code together. After that, every request that names a code for this resource returns `429 rate_limited`, a correct code included, until the oldest of those wrong codes is one hour old. `Retry-After` gives the seconds to wait. A correct code does not clear the count. Do not try codes: ask the requester for the code on their screen.

A code that is not six digits is not counted. A code sent while the resource has no pending request is not counted either. A `404` for a code that was already used does count, while another request is pending.

A resource can hold 256 approved passkeys. When it is full, the request returns `400 invalid_input`; remove one with `DELETE /v1/resources/{id}/allowed-passkeys/{device_id}` first.

Approval works for a request that was made before `access_requests` was turned off.

On `503`, nothing was changed. Wait for `Retry-After`, then send the request again.

#### Responses

##### Status: 200 The approved passkey

###### Content-Type: application/json

- **`data` (required)**

  `object` — A passkey you approved for a private resource.

  - **`approved_at` (required)**

    `string`, format: `date-time` — When you approved the request.

  - **`device_id` (required)**

    `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

  - **`name` (required)**

    `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "device_id": "k3wp-x2aq-7mzn-4tby",
    "name": "",
    "approved_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 410 The resource or access token is no longer available; check error.code

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Too many wrong codes were tried for this resource (\`rate\_limited\`). No code is accepted for it, a correct one included, until \`Retry-After\` has passed. The rate limit of your plan also answers \`429\`, with the code \`rate\_limit\_exceeded\`.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/allowed-passkeys/{device\_id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/allowed-passkeys/{device_id}`

### Remove an approved passkey

- **Method:** `DELETE`
- **Path:** `/v1/resources/{id}/allowed-passkeys/{device_id}`
- **Tags:** Resources

Removes an approved passkey from the resource. That passkey can no longer get a new link. A link it already has keeps working until it expires. The person can ask for access again if `access_requests` is on.

A `device_id` that no approved passkey of this resource has returns `404`.

On `503`, nothing was changed. Wait for `Retry-After`, then send the request again.

#### Responses

##### Status: 204 Passkey removed

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/access-requests

- **Method:** `PARAMETERS`
- **Path:** `/v1/access-requests`

### List pending access requests for all your resources

- **Method:** `GET`
- **Path:** `/v1/access-requests`
- **Tags:** Resources

Lists the pending access requests of all your resources, oldest first. Each row names its resource by `resource_id` and `crid`; use the `crid` to approve or deny the request.

Like the list of one resource, this list does not return the 6-digit codes. You approve with the code the requester gives you, and deny by `device_id`.

The list returns at most 1,000 requests and is not paged.

`meta.has_more` tells you whether the list is complete. `false` means that it holds every pending request you have. `true` means that it may be incomplete: either you have more than 1,000 pending requests, or the service stopped reading before it had looked at all of your resources. In the second case no request has to be missing, so do not read `true` as a count. When it is `true`, a request that is not in the list can be older than the ones that are.

The list of one resource (`GET /v1/resources/{id}/access-requests`) is always complete.

#### Responses

##### Status: 200 Pending access requests

###### Content-Type: application/json

- **`data` (required)**

  `array`

  **Items:**

  - **`crid` (required)**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`device_id` (required)**

    `string` — Identifies the requester's passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`.

  - **`expires_at` (required)**

    `string`, format: `date-time` — When the request is dropped if you do not decide it.

  - **`name` (required)**

    `string` — The name the requester typed. It is not verified. Display it as untrusted text.

  - **`requested_at` (required)**

    `string`, format: `date-time`

  - **`resource_id` (required)**

    `string` — Public key of the resource the request is for, retained for compatibility. Prefer crid for later requests.

- **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "name": "",
      "device_id": "k3wp-x2aq-7mzn-4tby",
      "requested_at": "",
      "expires_at": "",
      "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
      "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a"
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/qurls

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/qurls`

### List qURLs for a resource

- **Method:** `GET`
- **Path:** `/v1/resources/{id}/qurls`
- **Tags:** Resources

Lists the qURLs for the CRID in `{id}`. Use this operation for the complete list; resource detail responses contain at most 100 link summaries.

Pass `meta.next_cursor` as `cursor` until `meta.has_more` is false. A short page does not mean the list is complete. Keep all request parameters unchanged while paging.

Results have no guaranteed order, and active links do not come first. This operation has no filters or sort option; apply those in your client. On an unchanged result set, each link appears once. Links created or revoked while you page can be missed or move between pages; start again when you need a current list.

Integration-owned uploads (`type=transit`) return `404` here. Their link lists are not available through this management operation.

#### Responses

##### Status: 200 List of qURLs for the resource

###### Content-Type: application/json

- **`data`**

  `array`

  **Items:**

  - **`access_policy`**

    `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

    - **`ai_agent_policy`**

      `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

      - **`allow_categories`**

        `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`block_all`**

        `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

      - **`deny_categories`**

        `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`geo_allowlist`**

      `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`geo_denylist`**

      `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`ip_allowlist`**

      `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`ip_denylist`**

      `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`user_agent_allow_regex`**

      `string` — Regular expression to allow matching user agents (max 256 characters)

    - **`user_agent_deny_regex`**

      `string` — Regular expression to deny matching user agents (max 256 characters)

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`label`**

    `string`

  - **`max_sessions`**

    `integer`

  - **`one_time_use`**

    `boolean`

  - **`qurl_id`**

    `string`

  - **`qurl_site`**

    `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

  - **`session_duration`**

    `integer` — Session lifetime in seconds (0 = server default)

  - **`status`**

    `string`, possible values: `"active", "consumed", "expired", "revoked"`

  - **`target_path`**

    `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

  - **`use_count`**

    `integer`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "qurl_id": "",
      "label": "",
      "status": "active",
      "one_time_use": true,
      "max_sessions": 1,
      "session_duration": 1,
      "use_count": 1,
      "qurl_site": "",
      "target_path": "",
      "created_at": "",
      "expires_at": "",
      "access_policy": {
        "ip_allowlist": [
          "192.168.1.0/24",
          "10.0.0.1"
        ],
        "ip_denylist": [
          ""
        ],
        "geo_allowlist": [
          "US",
          "CA",
          "GB"
        ],
        "geo_denylist": [
          "CN",
          "RU"
        ],
        "user_agent_allow_regex": "^Mozilla.*",
        "user_agent_deny_regex": ".*bot.*",
        "ai_agent_policy": {
          "block_all": false,
          "deny_categories": [
            "gptbot",
            "commoncrawl",
            "bytedance"
          ],
          "allow_categories": [
            "claude",
            "chatgpt",
            "perplexity"
          ]
        }
      }
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 410 The resource is closed and this operation is no longer available. Use an active resource. The response can include \`meta.tombstone\` with retained resource metadata; clients must also handle its absence.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Create a qURL for an existing resource

- **Method:** `POST`
- **Path:** `/v1/resources/{id}/qurls`
- **Tags:** Resources

Creates a new qURL access link for the resource identified by its CRID in `{id}`. Use this endpoint when you already have a CRID; use `POST /v1/qurls` to create a qURL from a `target_url` directly.

A request body is required, but every field inside it is optional — to mint with the owner's plan defaults, send `{}`.

Errors:

- 404 `resource_not_found` if no resource with this ID exists.
- 403 `access_denied` if the resource exists but belongs to another owner.
- 403 `connector_disabled` if this is a qURL Connector resource (`type: tunnel` on the wire) and qURL Connector operations are currently unavailable.
- 400 `revoked` if the resource is revoked.
- 400 `invalid_input` in two cases. Read `detail` to tell them apart. The resource has passed its expiration: set a later expiration with `PATCH /v1/qurls/{id}` first. Or a field value is malformed or out of range (e.g. a `session_duration` like `5x`).
- 400 `validation_error` if `target_path` is JSON null or not a string.
- 400 `invalid_target_path` if a string `target_path` is empty, longer than 2048 UTF-8 bytes, malformed, or used with a stored resource that is not a qURL Connector resource.
- 400 `invalid_session_duration` if `session_duration` exceeds the resource's cap.

#### Request Body

##### Content-Type: application/json

- **`access_policy`**

  `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

  - **`ai_agent_policy`**

    `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

    - **`allow_categories`**

      `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`block_all`**

      `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

    - **`deny_categories`**

      `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

  - **`geo_allowlist`**

    `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`geo_denylist`**

    `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`ip_allowlist`**

    `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`ip_denylist`**

    `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`user_agent_allow_regex`**

    `string` — Regular expression to allow matching user agents (max 256 characters)

  - **`user_agent_deny_regex`**

    `string` — Regular expression to deny matching user agents (max 256 characters)

- **`expires_in`**

  `string` — How long the new link remains valid. Examples: \`5m\`, \`1h\`, \`7d\`, \`1w\`. Minimum: 1 minute. Maximum: 30 days on all plans. The resource's expiration can shorten the link's lifetime; read \`expires\_at\` in the response. When omitted or empty, qURL Connector links use 5 minutes. URL and integration-owned upload links use the service default. Set this field explicitly for a predictable lifetime.

- **`label`**

  `string` — Human-readable label identifying who this qURL is for.

- **`max_sessions`**

  `integer` — Maximum concurrent sessions for this qURL, not the whole resource. Use \`0\` for no limit or \`1\`–\`1000\` for a limit. Set \`one\_time\_use: false\` explicitly when you need multiple visitors: some resource types otherwise create a single-visitor link even when \`max\_sessions\` is \`0\`. Visitors that share a source IP count as one session and share its IP policy checks.

- **`one_time_use`**

  `boolean` — Whether the link is consumed by its first successful use. Explicit \`true\` or \`false\` takes precedence over the resource defaults. When omitted, URL and integration-owned upload links use \`false\`. qURL Connector links use \`true\` unless \`max\_sessions\` is greater than 1. For a reusable Connector link, explicitly send \`one\_time\_use: false\`, including when \`max\_sessions\` is \`0\` (unlimited).

- **`session_duration`**

  `string` — How long a session permits new requests after the recipient first reaches the content. Controls the session/cookie lifetime, separate from the qURL link expiration (expires\_in). Supports: seconds (30s), minutes (5m), hours (1h), days (1d). Minimum: 1 second. Maximum: 24 hours. Default: inherit the resource cap when set. Otherwise qURL Connectors use 1 hour and other resource types use the service default. New requests are denied at session expiry. Active requests, streams, and WebSockets are canceled at that deadline. See \`CreateQurlRequest.session\_duration\` → \*\*Timing precision\*\* for the full semantics.

- **`target_path`**

  `string` — Destination path and optional query for a qURL Connector link. For example, \`/view/abc?download=1\` redirects to that path on the resource's \`qurl.site\` host. Omit this field to use the host's root. Only qURL Connector resources (\`type=tunnel\`) accept it. Maximum: 2048 UTF-8 bytes. \*\*Access scope:\*\* The path permits itself and descendants at a segment boundary. \`/view/abc\` permits \`/view/abc/thumbnail\`, but not \`/view/abcd\`. The query does not restrict access. A scoped link works only through the resource host; access through a custom domain is denied. The Connector must also authorize the path before using it to look up an object. \*\*Path rules:\*\* Start with one \`/\`. Use canonical, unescaped ASCII. A single trailing slash is allowed. Do not use \`//\`, percent escapes, semicolons, empty interior segments, \`.\` or \`..\` segments, exclamation marks, parentheses, asterisks, apostrophes, a scheme, host, backslash, fragment, whitespace, or control characters. Use opaque route keys for objects; keep user filenames in metadata. Clients must preserve the accepted path bytes. Percent-encoded alternatives are denied. \*\*Query rules:\*\* Use ASCII letters, digits, \`.\_\~!$&()\*+,;=:@/?-\`, and well-formed percent escapes. Percent-encode other characters. Literal apostrophes are not accepted. Query bytes are preserved, including semicolons and percent escapes. \*\*Errors:\*\* JSON null and non-string values return \`400 validation\_error\`. Empty strings, oversized or malformed paths, and use on other resource types return \`400 invalid\_target\_path\`.

**Example:**

```json
{
  "expires_in": "1h",
  "one_time_use": true,
  "max_sessions": 0,
  "session_duration": "1h",
  "access_policy": {
    "ip_allowlist": [
      "192.168.1.0/24",
      "10.0.0.1"
    ],
    "ip_denylist": [
      ""
    ],
    "geo_allowlist": [
      "US",
      "CA",
      "GB"
    ],
    "geo_denylist": [
      "CN",
      "RU"
    ],
    "user_agent_allow_regex": "^Mozilla.*",
    "user_agent_deny_regex": ".*bot.*",
    "ai_agent_policy": {
      "block_all": false,
      "deny_categories": [
        "gptbot",
        "commoncrawl",
        "bytedance"
      ],
      "allow_categories": [
        "claude",
        "chatgpt",
        "perplexity"
      ]
    }
  },
  "label": "Alice from Acme",
  "target_path": "/view/9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}
```

#### Responses

##### Status: 201 qURL created successfully

###### Content-Type: application/json

- **`data`**

  `object` — Response data for qURL creation

  - **`branded_domain`**

    `string` — Custom hostname to display as link text. Keep \`qurl\_link\` as the destination. See \`MintLinkData.branded\_domain\` for domain status requirements. An idempotent retry returns the original value. Check the resource and domain again if you need the current domain status.

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`expires_at`**

    `string`, format: `date-time`

  - **`label`**

    `string` — Human-readable label for this qURL

  - **`qurl_id`**

    `string` — Identifier for this qURL. Save it to revoke or update this link, or match its webhook events.

  - **`qurl_link`**

    `string`, format: `uri` — Access link to share with the recipient. Store it securely; later GET requests do not return it. Exact request retries can replay the creation response within the replay window.

  - **`qurl_site`**

    `string`, format: `uri` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

  - **`target_path`**

    `string` — Redirect destination and authorization scope for this new qURL on a qURL Connector resource. Omitted when the new qURL uses the bare origin. Each create call makes an independent qURL; omission affects only that qURL.

  - **`type`**

    `string` — Resource type — echoes the type from the create request.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "qurl_id": "q_3a7f2c8e91b",
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "qurl_link": "",
    "branded_domain": "",
    "qurl_site": "",
    "expires_at": "",
    "target_path": "",
    "label": "",
    "type": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Forbidden while minting a qURL for a resource; inspect error.code

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 410 The resource is closed and this operation is no longer available. Use an active resource. The response can include \`meta.tombstone\` with retained resource metadata; clients must also handle its absence.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/qurls/{qurl\_id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/qurls/{qurl_id}`

### Revoke a specific qURL

- **Method:** `DELETE`
- **Path:** `/v1/resources/{id}/qurls/{qurl_id}`
- **Tags:** Resources

Revokes a specific qURL token and its live sessions, including sessions from consumed or expired links. Repeated revocation succeeds and completes any unfinished session cleanup. Other qURLs on the same resource are not affected. A 503 means revocation is committed but enforcement is pending. Retry the same delete after Retry-After to confirm all active caches were cleared.

#### Responses

##### Status: 204 qURL revoked

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 qURL status does not support revocation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Update a specific qURL

- **Method:** `PATCH`
- **Path:** `/v1/resources/{id}/qurls/{qurl_id}`
- **Tags:** Resources

Update expiry, label, access policy, max\_sessions, or session\_duration on a specific qURL token. The qURL must be active: a consumed, revoked, or expired qURL returns `409 revoked`. At least one field must be provided. Unlinked anonymous devices cannot set expiry more than 24 hours from now. An oversized extension returns `400 invalid_request`; an unavailable owner quota returns `503 service_unavailable` and can be retried.

Extending an active qURL keeps the same shared URL. `extend_by` adds to its currently enforced expiry; `expires_at` sets an absolute expiry. A concurrent expiry or session-policy change returns `409 conflict`; read the current values before retrying. Signed qURLs do not evaluate `access_policy`, so changing it returns `409 qurl_update_unsupported`; an empty `access_policy` clears a policy stored earlier, and resending the stored policy as returned (in any list order) is accepted. Changes to `expires_at`, `max_sessions`, and `session_duration` apply to new sessions; sessions that are already open keep their original lifetime. To end them, terminate them with `DELETE /v1/resources/{id}/sessions` or revoke the qURL. If the request fails with a 5xx, the update may already be in effect. Read the qURL back to see the enforced values. Repeating `extend_by` adds the duration again; retry an ambiguous extension with `expires_at` set to the intended target. If the link expires or is consumed first, PATCH returns 409 and does not apply new changes; reads still show its enforced expiry and limits. On a signed qURL link, any update also corrects a stored `expires_at`, `max_sessions`, or `session_duration` that differs from the value in effect, even if the request omits that field, and reports the corrected value.

#### Request Body

##### Content-Type: application/json

- **`access_policy`**

  `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

  - **`ai_agent_policy`**

    `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

    - **`allow_categories`**

      `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`block_all`**

      `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

    - **`deny_categories`**

      `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

  - **`geo_allowlist`**

    `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`geo_denylist`**

    `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`ip_allowlist`**

    `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`ip_denylist`**

    `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`user_agent_allow_regex`**

    `string` — Regular expression to allow matching user agents (max 256 characters)

  - **`user_agent_deny_regex`**

    `string` — Regular expression to deny matching user agents (max 256 characters)

- **`expires_at`**

  `string`, format: `date-time` — Absolute expiration timestamp (RFC 3339). Must be in the future and within 30 days. Mutually exclusive with extend\_by.

- **`extend_by`**

  `string` — Duration to extend by (e.g., "24h", "7d"). Minimum: 1 minute. Maximum: 30 days. Mutually exclusive with expires\_at.

- **`label`**

  `string` — Human-readable label for this qURL.

- **`max_sessions`**

  `integer` — Session limit for this qURL token (0 = unlimited). On signed qURL links it counts every session the link has admitted over its lifetime, not concurrent sessions: ending or terminating a session does not free a slot, and lowering the limit to the number of times the link has already been opened or fewer permanently prevents new sessions. Sessions that are already open are not affected.

- **`session_duration`**

  `string` — How long a session permits new requests after the recipient first reaches the content. Minimum: 1 second. Maximum: 24 hours. If the parent resource was created with a \`session\_duration\` (see \`ResourceData.session\_duration\_cap\`), values exceeding that cap are rejected with HTTP 400 \`invalid\_session\_duration\`. On a capped resource, sending an empty string for this field applies the cap rather than the server default — the uploader's intent supersedes the global default whenever a cap is in force. On an uncapped resource, an empty string resets to the default a new link would get: 1 hour for tunnel resources, and for URL resources the 24-hour-plus-10-second deadline described under \`CreateQurlRequest.session\_duration\`. New requests are denied at session expiry. Active requests, streams, and WebSockets are canceled at that deadline. See \`CreateQurlRequest.session\_duration\` → \*\*Timing precision\*\* for the full semantics.

**Example:**

```json
{
  "extend_by": "1h",
  "expires_at": "",
  "label": "",
  "access_policy": {
    "ip_allowlist": [
      "192.168.1.0/24",
      "10.0.0.1"
    ],
    "ip_denylist": [
      ""
    ],
    "geo_allowlist": [
      "US",
      "CA",
      "GB"
    ],
    "geo_denylist": [
      "CN",
      "RU"
    ],
    "user_agent_allow_regex": "^Mozilla.*",
    "user_agent_deny_regex": ".*bot.*",
    "ai_agent_policy": {
      "block_all": false,
      "deny_categories": [
        "gptbot",
        "commoncrawl",
        "bytedance"
      ],
      "allow_categories": [
        "claude",
        "chatgpt",
        "perplexity"
      ]
    }
  },
  "max_sessions": 0,
  "session_duration": "1h"
}
```

#### Responses

##### Status: 200 qURL updated

###### Content-Type: application/json

- **`data`**

  `object`

  - **`access_policy`**

    `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

    - **`ai_agent_policy`**

      `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

      - **`allow_categories`**

        `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`block_all`**

        `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

      - **`deny_categories`**

        `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`geo_allowlist`**

      `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`geo_denylist`**

      `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`ip_allowlist`**

      `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`ip_denylist`**

      `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`user_agent_allow_regex`**

      `string` — Regular expression to allow matching user agents (max 256 characters)

    - **`user_agent_deny_regex`**

      `string` — Regular expression to deny matching user agents (max 256 characters)

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`label`**

    `string`

  - **`max_sessions`**

    `integer`

  - **`one_time_use`**

    `boolean`

  - **`qurl_id`**

    `string`

  - **`qurl_site`**

    `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

  - **`session_duration`**

    `integer` — Session lifetime in seconds (0 = server default)

  - **`status`**

    `string`, possible values: `"active", "consumed", "expired", "revoked"`

  - **`target_path`**

    `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

  - **`use_count`**

    `integer`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "qurl_id": "",
    "label": "",
    "status": "active",
    "one_time_use": true,
    "max_sessions": 1,
    "session_duration": 1,
    "use_count": 1,
    "qurl_site": "",
    "target_path": "",
    "created_at": "",
    "expires_at": "",
    "access_policy": {
      "ip_allowlist": [
        "192.168.1.0/24",
        "10.0.0.1"
      ],
      "ip_denylist": [
        ""
      ],
      "geo_allowlist": [
        "US",
        "CA",
        "GB"
      ],
      "geo_denylist": [
        "CN",
        "RU"
      ],
      "user_agent_allow_regex": "^Mozilla.*",
      "user_agent_deny_regex": ".*bot.*",
      "ai_agent_policy": {
        "block_all": false,
        "deny_categories": [
          "gptbot",
          "commoncrawl",
          "bytedance"
        ],
        "allow_categories": [
          "claude",
          "chatgpt",
          "perplexity"
        ]
      }
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 qURL is not active (\`revoked\`), its expiry or session policy changed concurrently (\`conflict\`), or the link cannot accept the change (\`qurl\_update\_unsupported\`)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/share

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/share`

### Share a resource by CRID

- **Method:** `POST`
- **Path:** `/v1/resources/{id}/share`
- **Tags:** Resources

Creates a fresh access link for a resource. Put its CRID in `id`. This API creates a link for the resource's owner or, for a private resource, for a device the publisher allowed. Any other caller gets the same `404 resource_not_found` as for a CRID that names nothing. A CRID alone grants no access. Every request needs the `qurl:resolve` scope. Send `{}` to use the defaults. Set `ttl_seconds` for the link lifetime or `session_duration` for the lifetime of each admitted session. Link expiry does not end an already-admitted session.

The response contains the secret link in `qurl`. Share it only with the intended recipient. Save `qurl_id` to revoke this link with `DELETE /v1/resources/{id}/qurls/{qurl_id}` using the resource CRID and owner credentials. The resource owner retains control of the resource and its links.

Each call creates a new link. This operation does not promise idempotent replay; a retry can create another link. Links expire on their own. Request-rate limits and the resource owner's enforced monthly data-transfer allowance apply; there is no per-resource link quantity cap.

Requested lifetimes are shortened as needed to fit the 30-day platform maximum and the resource's expiration. Unlinked anonymous devices are also capped at 24 hours. Check `expires_at` and `expires_in_seconds` for the effective lifetime. Later GET requests do not return the secret link.

A successful response also says who published the resource and when the resource was created: `publisher` and `resource_created_at`. The second is the age of the resource, not of this link, which was minted just now. `publisher.verified` is `true` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. An unverified publisher's `name` is self-declared: the publisher chose it and nobody has confirmed it. Show it as unverified, and do not treat it as proof of who published the resource. These two fields are reported by the service; verifying a CRID or a link does not verify them. Error responses never include them.

| Status and code           | What to do                                                                                                                                           |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `503 service_unavailable` | Temporary access links are unavailable in this environment.                                                                                          |
| `401 unauthorized`        | Send valid credentials.                                                                                                                              |
| `403 insufficient_scope`  | Use a credential with qurl:resolve scope.                                                                                                            |
| `503 connector_stopped`   | Start the Connector before sharing.                                                                                                                  |
| `404 resource_not_found`  | Check the CRID and the credential. Missing and retired resources return this error, and so does every resource this caller cannot create a link for. |
| `410`                     | The resource is closed; use a live resource.                                                                                                         |
| `400 revoked`             | The resource is revoked; use a live resource.                                                                                                        |
| `400 invalid_input`       | The resource has passed its expiration; its owner can set a later one.                                                                               |
| `403 quota_exceeded`      | Resolve the monthly data-transfer limit before retrying.                                                                                             |

#### Request Body

##### Content-Type: application/json

- **`session_duration`**

  `string` — Lifetime of each admitted session, independent of the link lifetime. Uses the same duration syntax and 1-second to 24-hour bounds as mint\_link. The resource's session-duration cap still applies; exceeding it returns \`400 invalid\_session\_duration\`. Omit this field to preserve the resource or service default. The session starts on first authorized content access; initial network admission includes the existing 10-second arrival allowance.

- **`ttl_seconds`**

  `integer` — Requested link lifetime in whole seconds. Omit it to use the service default. Values from 1 to 59 are raised to 60 seconds; values above 2,592,000 are lowered to 30 days. Zero, negative values, and non-integers fail request validation. The resource's expiration can shorten the result further. Read \`expires\_at\` and \`expires\_in\_seconds\` in the response for the effective lifetime.

**Example:**

```json
{
  "ttl_seconds": 300,
  "session_duration": "5m"
}
```

#### Responses

##### Status: 200 A fresh share link was minted for the resource. The \`qurl\` value is returned once and never retrievable again — capture it now, along with the \`qurl\_id\` that revokes it. \`publisher\` and \`resource\_created\_at\` describe the resource's publisher and when the resource was created (not when this link was minted); \`publisher.verified: false\` means the publisher's name is self-declared and has not been confirmed.

###### Content-Type: application/json

- **`data`**

  `object`

  - **`expires_at` (required)**

    `string`, format: `date-time` — When the share link stops working — the effective, post-clamp expiry.

  - **`expires_in_seconds` (required)**

    `integer` — Seconds until \`expires\_at\`, measured when the response was produced. This is the effective lifetime after clamping, so it may be lower than the requested \`ttl\_seconds\`.

  - **`publisher` (required)**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`qurl` (required)**

    `string`, format: `uri` — The freshly minted share link — a short-lived qURL access link. Returned once and never retrievable again; whoever holds the link can use it, so treat it as a secret and hand it only to the intended recipient.

  - **`qurl_id` (required)**

    `string` — Identifier for the new qURL. Save it to revoke this link with \`DELETE /v1/resources/{id}/qurls/{qurl\_id}\` using the resource CRID, and to match access events.

  - **`resource_created_at` (required)**

    `string`, format: `date-time` — When the shared resource was created. This is not when this link was minted: the link is new, and \`expires\_at\` is the link's expiry.

  - **`single_use` (required)**

    `boolean` — Whether the share link is consumed by its first use. Set from the resource type's minting defaults (qURL Connector links default to single-visitor use).

  - **`type` (required)**

    `string` — The shared resource's type (see \`ResourceType\` for the full enum).

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "qurl_id": "q_a1b2c3d4e5f",
    "qurl": "https://qurl.link/#qv2t1.1.1.1.Y2xhaW1zLWV4YW1wbGU.c2VjcmV0LWV4YW1wbGU.c2lnbmF0dXJlLWV4YW1wbGU",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "type": "url",
    "expires_at": "2026-03-09T15:35:00Z",
    "expires_in_seconds": 300,
    "single_use": false,
    "resource_created_at": "2026-03-01T09:00:00Z",
    "publisher": {
      "name": "Acme Docs",
      "verified": false
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 410 The resource is closed and this operation is no longer available. Use an active resource. No retained resource metadata is disclosed.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Temporary access links cannot be minted now. This response is returned with \`service\_unavailable\` when the environment has not enabled temporary links, or with \`connector\_stopped\` when the addressed qURL Connector is stopped. Clients must use the code to distinguish a retryable environment condition from the required owner action.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/sessions

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/sessions`

### List active sessions for a resource

- **Method:** `GET`
- **Path:** `/v1/resources/{id}/sessions`
- **Tags:** Sessions

Returns active access sessions for the specified resource. Use `paged=true` for bounded pages. `user_agent` and `last_seen_at` are omitted when they were not recorded for a session, and `qurl_id` is omitted when the qURL's record cannot be read. Continue with `meta.next_cursor` until `meta.has_more` is false, even when a page is empty. A paged request examines at most 100 stored sessions. Paging also examines past sessions, so the number of requests depends on session history, not just the number of active visitors. Without `paged=true` or a cursor, the operation collects every page within a 28-second total budget. Failure never reports partial work as full success.

#### Responses

##### Status: 200 Active sessions

###### Content-Type: application/json

- **`data`**

  `array`

  **Items:**

  - **`created_at`**

    `string`, format: `date-time`

  - **`last_seen_at`**

    `string`, format: `date-time`

  - **`qurl_id`**

    `string` — qURL display ID (q\_ prefix) that created this session

  - **`session_id`**

    `string` — Unique session identifier

  - **`src_ip`**

    `string` — Client IP that created the session

  - **`user_agent`**

    `string` — Client user agent

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "session_id": "",
      "qurl_id": "",
      "src_ip": "",
      "user_agent": "",
      "created_at": "",
      "last_seen_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Terminate all active sessions for a resource

- **Method:** `DELETE`
- **Path:** `/v1/resources/{id}/sessions`
- **Tags:** Sessions

Terminates sessions; use `paged=true` to process one bounded page and returns how many were terminated. A session opened at the same time can survive; revoke the qURL to stop new sessions. Sessions opened through delegated qURLs are managed with `DELETE /v1/delegated-qurls/{qurl_id}` instead. This ends current visits; it does not revoke the resource's qURLs. A visitor can open a qURL again unless it is single-use, has reached its `max_sessions`, or has expired, and terminating a session does not free a `max_sessions` slot. To stop future access through a qURL, revoke it with `DELETE /v1/resources/{id}/qurls/{qurl_id}`.

If the request fails with a 5xx, some sessions may already be terminated; the request is safe to repeat.

Termination of sessions opened through signed qURL links also sends a session revocation to NHP. Success confirms the configured NHP delivery contract; delivery failures must be retried. A visitor who also has a session open through another of this resource's qURLs from the same network address can keep access through that session; terminate all sessions or revoke the qURLs to be certain. Continue with `meta.next_cursor` until `meta.has_more` is false, even when a page is empty. A paged request examines at most 8 stored sessions. Paging also examines past sessions, so the number of requests depends on session history, not just the number of active visitors. Without `paged=true` or a cursor, the operation collects every page within a 28-second total budget. Failure never reports partial work as full success. If an unpaged request fails, retry with `paged=true` and follow continuations to give each page its own budget.

#### Responses

##### Status: 200 Sessions terminated

###### Content-Type: application/json

- **`data`**

  `object`

  - **`terminated`**

    `integer` — Number of sessions terminated

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": {
    "terminated": 1
  },
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/resources/{id}/sessions/{session\_id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/resources/{id}/sessions/{session_id}`

### Terminate a specific session

- **Method:** `DELETE`
- **Path:** `/v1/resources/{id}/sessions/{session_id}`
- **Tags:** Sessions

Terminates a specific active session and confirms NHP delivery for signed qURL links. Terminating an already ended session succeeds; a session whose record has aged out returns 404. The qURL that opened the session stays active; see `DELETE /v1/resources/{id}/sessions` for what a visitor can do next and when the termination takes effect.

#### Responses

##### Status: 204 Session terminated

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/quota

- **Method:** `PARAMETERS`
- **Path:** `/v1/quota`

### Get quota information

- **Method:** `GET`
- **Path:** `/v1/quota`
- **Tags:** Quota

Retrieves usage quota for the authenticated user.

#### Responses

##### Status: 200 Quota information

###### Content-Type: application/json

- **`data`**

  `object`

  - **`period_end`**

    `string`, format: `date-time`

  - **`period_start`**

    `string`, format: `date-time`

  - **`plan`**

    `string`, possible values: `"free", "growth", "enterprise"`

  - **`rate_limits`**

    `object`

    - **`create_per_hour`**

      `integer`

    - **`create_per_minute`**

      `integer`

    - **`list_per_minute`**

      `integer`

    - **`max_active_qurls`**

      `integer` — Deprecated — use \`max\_active\_resources\`, whose value this mirrors. Despite the name it has always reported the protected-resource cap, never a qURL cap. For the qURL cap, read \`max\_qurls\`.

    - **`max_active_resources`**

      `integer` — Maximum active protected resources — the target URLs and qURL Connectors you protect (-1 = unlimited). Reached only when protecting something new; creating more qURLs for an existing resource does not use this allowance. Other limits, access rules, and resource status still apply.

    - **`max_data_transfer_bytes`**

      `integer`, format: `int64` — Maximum data proxied through your protected resources per calendar month, in bytes (-1 = unlimited).

    - **`max_expiry_seconds`**

      `integer` — Maximum expiry duration in seconds (2592000, or 30 days, on all customer plans)

    - **`max_qurls`**

      `integer` — Maximum qURLs you may hold across all your protected resources (-1 = unlimited). Currently unlimited on every plan — read the live value rather than assuming it. \`max\_active\_resources\` bounds how many things you may protect. It does not limit how many qURLs you may issue for one protected resource.

    - **`max_tokens_per_qurl`**

      `integer` — Deprecated compatibility field. Always -1 (unlimited); there is no per-resource qURL cap.

    - **`resolve_per_minute`**

      `integer` — Sustained token resolution refill rate per minute (-1 = unlimited). The instantaneous burst allowance can be smaller; X-RateLimit-Limit reports that burst allowance.

  - **`usage`**

    `object`

    - **`active_qurls`**

      `integer` — Deprecated — use \`active\_resources\`, whose value this mirrors. Despite the name it has always reported the count of active protected resources.

    - **`active_qurls_percent`**

      `number | null`, format: `float` — Deprecated — use \`active\_resources\_percent\`, whose value this mirrors, including the fact that it can exceed 100 and that null means either "unlimited" or "plan could not be determined".

    - **`active_resources`**

      `integer` — Currently active protected resources

    - **`active_resources_percent`**

      `number | null`, format: `float` — Percentage of max\_active\_resources used. Null in TWO cases, which a client must not conflate: your plan is unlimited, or your plan could not be determined right now — in the second case the limits reported alongside it are placeholders, so do not render "unlimited" on a null alone. Can exceed 100: the cap is applied when you protect something new, so an account already above it keeps everything it has and reports over 100 until it drops back under.

    - **`data_transfer_available`**

      `boolean` — Whether traffic measurement is enabled and the account usage was resolved.

    - **`data_transfer_bytes`**

      `integer`, format: `int64` — Request and response bytes proxied in the current UTC calendar month. Recent traffic can take a short time to appear. Use only when data\_transfer\_available is true.

    - **`qurls_created`**

      `integer` — Total recorded qURL creations and link mints this UTC calendar month, including share links. Counts update asynchronously.

    - **`total_accesses`**

      `integer` — Total access attempts this period

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "plan": "free",
    "period_start": "",
    "period_end": "",
    "rate_limits": {
      "create_per_minute": 1,
      "create_per_hour": 1,
      "list_per_minute": 1,
      "resolve_per_minute": 1,
      "max_qurls": 1,
      "max_active_resources": 1,
      "max_data_transfer_bytes": 1,
      "max_expiry_seconds": 1
    },
    "usage": {
      "qurls_created": 1,
      "active_resources": 1,
      "active_resources_percent": null,
      "data_transfer_bytes": 1,
      "data_transfer_available": true,
      "total_accesses": 1
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 304 Not Modified (ETag match)

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/me

- **Method:** `PARAMETERS`
- **Path:** `/v1/me`

### Get the authenticated identity

- **Method:** `GET`
- **Path:** `/v1/me`
- **Tags:** Identity

Echoes who the caller is authenticated as, derived entirely from the presented credential — no account, billing, or usage state is consulted (see `GET /v1/quota` for plan and usage). Use it to validate a credential and discover its identity: the owning account, the authentication method, and — for API-key callers — the key's identifier, kind, granted scopes, and expiration.

Any valid credential may ask who it is: the endpoint accepts every scope rather than requiring a specific one. One-shot enrollment tokens and device credentials are restricted to their enrollment and connector operations and cannot call this endpoint.

A frozen account receives 403 with error code `account_frozen` — itself an informative answer about the account's standing.

#### Responses

##### Status: 200 The authenticated identity

###### Content-Type: application/json

- **`data`**

  `object`

  - **`auth_type` (required)**

    `string` — How this request authenticated: a qURL API key (\`api\_key\`) or an OAuth 2.1 JWT (\`jwt\`).

  - **`owner_id` (required)**

    `string` — Identifier of the account the credential belongs to.

  - **`api_key`**

    `object` — Identity of the API key that authenticated this request. Present only when \`auth\_type\` is \`api\_key\`; JWT-authenticated requests omit it.

    - **`key_id` (required)**

      `string`

    - **`kind` (required)**

      `string` — The credential kind: a durable \`api\_key\`, a one-shot \`enrollment\_token\`, or a system-minted per-device \`device\` credential. Unknown future kind values may appear as the credential model grows; clients must tolerate values beyond these.

    - **`scopes` (required)**

      `array` — Permissions granted to this credential. Returned in alphabetical order for deterministic responses.

      **Items:**

      `string`

    - **`expires_at`**

      `string`, format: `date-time` — Expiration timestamp. Omitted for non-expiring credentials.

    - **`key_prefix`**

      `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "owner_id": "auth0|user123",
    "auth_type": "api_key",
    "api_key": {
      "key_id": "",
      "key_prefix": "",
      "kind": "api_key",
      "scopes": [
        ""
      ],
      "expires_at": ""
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/me/publisher

- **Method:** `PARAMETERS`
- **Path:** `/v1/me/publisher`

### Get your publisher profile

- **Method:** `GET`
- **Path:** `/v1/me/publisher`
- **Tags:** Publisher

Returns your publisher profile: the name people see when they request a link to one of your resources, and whether qURL has verified you.

`verified` is `true` only when qURL has confirmed who you are. You cannot apply for verification yet, and no request can set it, so expect `false`. While `verified` is `false`, `name` is self-declared: you chose it and nobody has confirmed it. `name` is omitted when you have not set one.

A stored name that no longer satisfies the name rules is withheld and reads as no name; set it again to see which rule it breaks.

Any credential for the owner works: an account access token, an account API key, or a registered device credential. Every device has its own profile, separate from the account it is linked to. To read a linked device's profile from the account, sign in with the account and send the device owner in `X-QURL-Owner`. A frozen account receives 403 with error code `account_frozen`.

#### Responses

##### Status: 200 Your publisher profile

###### Content-Type: application/json

- **`data`**

  `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

  - **`verified` (required)**

    `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

  - **`name`**

    `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "name": "Acme Docs",
    "verified": false
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Set or remove your publisher name

- **Method:** `PATCH`
- **Path:** `/v1/me/publisher`
- **Tags:** Publisher

Sets the name shown as the publisher of every resource you own. Send an empty string to remove it. The name appears on share responses and on your own resource reads; a change can take a few minutes to appear everywhere.

A name is optional, and none is set by default. Everyone who requests a link to any of your resources sees the same name, so setting one lets people tell that those resources come from the same publisher. Leave it unset if you do not want your resources connected to each other this way.

The name is self-declared. Setting it does not verify you: `verified` does not change, cannot be sent in this request, and a request that includes it is rejected with 400. Unless qURL has verified you, everyone who sees your name also sees that it is unverified.

A name must:

- be 1 to 64 characters, in Unicode NFC form;
- use only letters, digits, single spaces, and `. , ' & - _ ( ) / + ! @ # :`;
- contain at least one letter or digit;
- attach accents and other combining marks to a letter or digit, with no more than 3 on any one character;
- not start or end with a space;
- not contain emoji, symbols, quotation marks, characters that look like quotation marks or apostrophes, superscript letters, or invisible characters;
- not spell a reserved word: `verified`, `LayerV`, or `qURL`. The check ignores letter case, accents, spaces and punctuation, so `Veri-fied`, `V E R I F I E D`, `Layer V` and `Q-URL` are refused too. Only a publisher that qURL has verified may use these words.

A name that breaks a rule returns 400 with error code `invalid_input`. Sending the same name again is safe and returns the same result. A stored name that no longer satisfies these rules is withheld and reads as no name; set it again to see which rule it breaks.

Any credential for the owner works: an account access token, an account API key, or a registered device credential. Every device has its own profile. If a linked device's name is wrong, sign in with the account, send the device owner in `X-QURL-Owner`, and set or remove the name. A frozen account receives 403 with error code `account_frozen`.

#### Request Body

##### Content-Type: application/json

- **`name` (required)**

  `string` — The publisher name to show, or an empty string to remove it. See the operation description for the rules a name must follow.

**Example:**

```json
{
  "name": "Acme Docs"
}
```

#### Responses

##### Status: 200 Your updated publisher profile. \`verified: false\` means the name is self-declared and has not been confirmed.

###### Content-Type: application/json

- **`data`**

  `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

  - **`verified` (required)**

    `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

  - **`name`**

    `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "name": "Acme Docs",
    "verified": false
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/usage/current-period

- **Method:** `PARAMETERS`
- **Path:** `/v1/usage/current-period`

### Get current billing period usage

- **Method:** `GET`
- **Path:** `/v1/usage/current-period`
- **Tags:** Usage

Returns usage statistics for the current billing period, including recorded qURL creations and link mints (including share links), the user's tier, period dates, and a cost estimate for usage-based plans (Growth tier). Counts update asynchronously. Requires an Auth0 access token (JWT).

#### Responses

##### Status: 200 Current period usage information

###### Content-Type: application/json

- **`data`**

  `object`

  - **`active_qurls` (required)**

    `integer` — Deprecated — use \`active\_resources\`, whose value this mirrors. Despite the name it has always reported the count of active protected resources.

  - **`active_resources` (required)**

    `integer` — Number of currently active protected resources — the target URLs and qURL Connectors you protect.

  - **`data_transfer_bytes` (required)**

    `integer`, format: `int64` — Data proxied through your protected resources in the current UTC calendar month, in bytes. This is independent of the stored Stripe billing period. Reflects traffic measured by the metering pipeline; reads \`0\` where measurement is not yet enabled, and can lag the most recent traffic by a short interval.

  - **`period_end` (required)**

    `string`, format: `date-time` — Exclusive end of the stored Stripe billing period, or exclusive end of the current UTC calendar month when no Stripe period is stored

  - **`period_start` (required)**

    `string`, format: `date-time` — Start of the stored Stripe billing period, or the current UTC calendar month when no Stripe period is stored

  - **`qurls_created` (required)**

    `integer` — Committed metered usage in the stored Stripe billing period, or recorded qURL creations and link mints in the current UTC calendar month when no Stripe period is stored

  - **`tier` (required)**

    `string`, possible values: `"free", "growth", "enterprise"` — Current billing tier

  - **`unit_price_cents` (required)**

    `integer`, format: `int64` — Stored Stripe unit price in cents; zero when no metered price is stored

  - **`cost_estimate`**

    `object`

    - **`amount_cents` (required)**

      `integer`, format: `int64` — Cost in cents

    - **`currency` (required)**

      `string` — Currency code (e.g. "usd")

    - **`description` (required)**

      `string` — Human-readable breakdown

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "tier": "free",
    "period_start": "",
    "period_end": "",
    "qurls_created": 1,
    "unit_price_cents": 0,
    "active_resources": 1,
    "data_transfer_bytes": 1,
    "cost_estimate": {
      "currency": "usd",
      "amount_cents": 1500,
      "description": "150 qURLs x $0.10/qURL"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/usage/daily

- **Method:** `PARAMETERS`
- **Path:** `/v1/usage/daily`

### Get daily usage breakdown

- **Method:** `GET`
- **Path:** `/v1/usage/daily`
- **Tags:** Usage

Returns a daily breakdown of recorded qURL creations and link mints (including share links) for the stored Stripe billing period on paid Stripe-backed accounts. Free and non-Stripe Enterprise accounts use the current UTC calendar month. Periods are half-open: period\_start is inclusive and period\_end is exclusive. Counts update asynchronously. Requires JWT authentication (dashboard endpoint).

#### Responses

##### Status: 200 Daily usage breakdown

###### Content-Type: application/json

- **`data`**

  `object`

  - **`daily` (required)**

    `array` — One entry per UTC date that overlaps the elapsed portion of the half-open billing period

    **Items:**

    - **`date` (required)**

      `string`, format: `date` — Calendar date (YYYY-MM-DD)

    - **`qurls_created` (required)**

      `integer` — Recorded qURL creations and link mints on this UTC date, including share links. Counts update asynchronously.

  - **`period_end` (required)**

    `string`, format: `date-time` — Exclusive end of the stored Stripe billing period, or exclusive end of the current UTC calendar month when no Stripe period is stored

  - **`period_start` (required)**

    `string`, format: `date-time` — Start of the stored Stripe billing period, or the current UTC calendar month when no Stripe period is stored

  - **`tier` (required)**

    `string`, possible values: `"free", "growth", "enterprise"` — Current billing tier

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "tier": "free",
    "period_start": "",
    "period_end": "",
    "daily": [
      {
        "date": "",
        "qurls_created": 1
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Record signup discovery source

- **Method:** `PUT`
- **Path:** `/v1/me/signup-attribution`
- **Tags:** Customer

Records how the authenticated user discovered qURL. Available on all plans. Requires an Auth0 access token with qurl:write; API keys are not supported. The first answer is permanent. Every subsequent submission, including an identical retry, returns 409 without changing the saved answer. Read it from GET /v1/customer. No customer identifier is accepted in the request. Frozen accounts cannot submit an answer.

#### Request Body

##### Content-Type: application/json

- **`source` (required)**

  `string`, possible values: `"my_ai_told_me", "ai_search", "web_search", "friend_or_coworker", "social_media", "other"` — How you discovered qURL.

**Example:**

```json
{
  "source": "my_ai_told_me"
}
```

#### Responses

##### Status: 204 Answer saved; no response body.

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Get your preferences

- **Method:** `GET`
- **Path:** `/v1/me/preferences`
- **Tags:** Customer

Returns the signed-in user's preferences. Today that is which optional email LayerV may send: product updates, and announcements and promotions. Account and security notices are always sent and are not a preference.

`email.updated_at` and `email.source` are null until a choice has been saved. Until then both email preferences read as `false`.

Requires an Auth0 access token with qurl:read; API keys are not supported. A frozen account can still read its preferences.

#### Responses

##### Status: 200 Your preferences

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`email` (required)**

    `object`

    - **`product_updates` (required)**

      `boolean` — Whether to send email about new features and product changes.

    - **`promotions` (required)**

      `boolean` — Whether to send announcements, events and offers.

    - **`source` (required)**

      `string | null`, possible values: `"signup_survey", "dashboard", null` — Where the email choice was last saved, or null if it never was.

    - **`updated_at` (required)**

      `string | null`, format: `date-time` — When the email choice was last saved, or null if it never was.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "email": {
      "product_updates": true,
      "promotions": true,
      "updated_at": null,
      "source": "signup_survey"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Change your preferences

- **Method:** `PATCH`
- **Path:** `/v1/me/preferences`
- **Tags:** Customer

Changes only the preferences you send and returns all of them. A preference you leave out keeps its stored value.

`email.source` says where the choice was made and is stored with the time of the change. `dashboard` always saves. `signup_survey` saves only while no email choice has been saved yet; afterwards it returns 409 and changes nothing, so a survey shown again can never undo a later choice.

Sending the same `dashboard` change again is safe and returns the same result. A repeated `signup_survey` change returns 409, including a retry of one whose first response was lost: read the stored choice with GET. Requires an Auth0 access token with qurl:write; API keys are not supported. No customer identifier is accepted in the request. A frozen account can still change its preferences, so it can always opt out of email.

#### Request Body

##### Content-Type: application/json

- **`email` (required)**

  `object` — At least one of \`product\_updates\` and \`promotions\` is required.

  - **`source` (required)**

    `string`, possible values: `"signup_survey", "dashboard"` — Where an email choice was made. \`signup\_survey\` can only save the first choice; \`dashboard\` can always save.

  - **`product_updates`**

    `boolean`

  - **`promotions`**

    `boolean`

**Example:**

```json
{
  "email": {
    "product_updates": true,
    "promotions": true,
    "source": "signup_survey"
  }
}
```

#### Responses

##### Status: 200 Your preferences after the change

###### Content-Type: application/json

- **`data` (required)**

  `object`

  - **`email` (required)**

    `object`

    - **`product_updates` (required)**

      `boolean` — Whether to send email about new features and product changes.

    - **`promotions` (required)**

      `boolean` — Whether to send announcements, events and offers.

    - **`source` (required)**

      `string | null`, possible values: `"signup_survey", "dashboard", null` — Where the email choice was last saved, or null if it never was.

    - **`updated_at` (required)**

      `string | null`, format: `date-time` — When the email choice was last saved, or null if it never was.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "email": {
      "product_updates": true,
      "promotions": true,
      "updated_at": null,
      "source": "signup_survey"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The request conflicts with an existing operation

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Get customer profile

- **Method:** `GET`
- **Path:** `/v1/customer`
- **Tags:** Customer

Returns the authenticated user's customer profile including tier, spending cap, usage count, and frozen status. Creates a Free account profile on first access. Requires an Auth0 access token (JWT). API key authentication is not supported.

#### Responses

##### Status: 200 Customer profile

###### Content-Type: application/json

- **`data`**

  `object`

  - **`current_period_usage` (required)**

    `integer`, format: `int64` — Usage count in current billing period

  - **`frozen` (required)**

    `boolean` — Whether the account is frozen

  - **`signup_discovery_source` (required)**

    `string | null` — Saved signup discovery source, or null if no answer has been recorded.

  - **`spending_cap_cents` (required)**

    `integer`, format: `int64` — Spending cap in cents (0 = no cap)

  - **`tier` (required)**

    `string`, possible values: `"free", "growth", "enterprise"` — Customer's billing tier

  - **`frozen_reason`**

    `string | null`, possible values: `"spending_cap", "payment_failed", "manual"` — Reason for account freeze

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "signup_discovery_source": null,
    "tier": "free",
    "spending_cap_cents": 1,
    "current_period_usage": 1,
    "frozen": true,
    "frozen_reason": "spending_cap"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Update customer settings

- **Method:** `PATCH`
- **Path:** `/v1/customer`
- **Tags:** Customer

Updates customer settings. Currently supports spending cap. Requires JWT authentication and growth or enterprise tier. API key authentication is not supported.

#### Request Body

##### Content-Type: application/json

- **`spending_cap_cents` (required)**

  `integer`, format: `int64` — New spending cap in cents (0 = no cap)

**Example:**

```json
{
  "spending_cap_cents": 0
}
```

#### Responses

##### Status: 200 Updated customer profile

###### Content-Type: application/json

- **`data`**

  `object`

  - **`current_period_usage` (required)**

    `integer`, format: `int64` — Usage count in current billing period

  - **`frozen` (required)**

    `boolean` — Whether the account is frozen

  - **`signup_discovery_source` (required)**

    `string | null` — Saved signup discovery source, or null if no answer has been recorded.

  - **`spending_cap_cents` (required)**

    `integer`, format: `int64` — Spending cap in cents (0 = no cap)

  - **`tier` (required)**

    `string`, possible values: `"free", "growth", "enterprise"` — Customer's billing tier

  - **`frozen_reason`**

    `string | null`, possible values: `"spending_cap", "payment_failed", "manual"` — Reason for account freeze

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "signup_discovery_source": null,
    "tier": "free",
    "spending_cap_cents": 1,
    "current_period_usage": 1,
    "frozen": true,
    "frozen_reason": "spending_cap"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Create Stripe checkout session

- **Method:** `POST`
- **Path:** `/v1/billing/checkout`
- **Tags:** Billing

Creates a Stripe Checkout session for plan upgrade. Returns a URL to redirect the user to Stripe's hosted checkout page. Requires an Auth0 access token (JWT). API key authentication is not supported.

The `plan` field selects which paid plan to check out into. Only purchasable plans are permitted (currently: `growth`); any other value is rejected with 400.

Customers with an existing Stripe subscription or another pending checkout are rejected with 409. Existing subscriptions must be managed through the billing portal; pending attempts must reuse their original Idempotency-Key.

#### Request Body

##### Content-Type: application/json

- **`plan` (required)**

  `string`, possible values: `"growth"` — Paid plan to check out into. Only purchasable plans are permitted (currently: \`growth\`); anything else is rejected as an invalid enum value.

**Example:**

```json
{
  "plan": "growth"
}
```

#### Responses

##### Status: 200 Checkout session created

###### Content-Type: application/json

- **`data`**

  `object`

  - **`url` (required)**

    `string`, format: `uri` — Stripe Checkout URL to redirect user to

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "url": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 A billing subscription or checkout already exists

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Create Stripe billing portal session

- **Method:** `POST`
- **Path:** `/v1/billing/portal`
- **Tags:** Billing

Creates a Stripe Billing Portal session for managing subscriptions, payment methods, and invoices. Returns a URL to redirect the user. Requires an Auth0 access token (JWT). API key authentication is not supported.

#### Responses

##### Status: 200 Portal session created

###### Content-Type: application/json

- **`data`**

  `object`

  - **`url` (required)**

    `string`, format: `uri` — Stripe Billing Portal URL to redirect user to

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "url": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### List invoices

- **Method:** `GET`
- **Path:** `/v1/billing/invoices`
- **Tags:** Billing

Returns the authenticated user's Stripe invoices. Requires an Auth0 access token (JWT). API key authentication is not supported.

#### Responses

##### Status: 200 Invoice list

###### Content-Type: application/json

- **`data`**

  `object`

  - **`invoices` (required)**

    `array`

    **Items:**

    - **`amount_cents` (required)**

      `integer`, format: `int64` — Invoice amount in cents

    - **`created_at` (required)**

      `string`, format: `date-time` — When the invoice was created

    - **`id` (required)**

      `string` — Stripe invoice ID

    - **`status` (required)**

      `string`, possible values: `"paid", "open", "void", "draft"` — Invoice status

    - **`pdf_url`**

      `string | null`, format: `uri` — URL to download the invoice PDF

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": {
    "invoices": [
      {
        "id": "",
        "amount_cents": 1,
        "status": "paid",
        "created_at": "",
        "pdf_url": null
      }
    ]
  },
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Register a custom domain

- **Method:** `POST`
- **Path:** `/v1/domains`
- **Tags:** Domains

Registers a new custom domain for the authenticated user. Returns DNS records that must be configured before verification. Requires enterprise plan.

#### Request Body

##### Content-Type: application/json

- **`domain` (required)**

  `string` — The custom domain name to register

**Example:**

```json
{
  "domain": "secure.example.com"
}
```

#### Responses

##### Status: 201 Domain registered successfully

###### Content-Type: application/json

- **`data`**

  `object`

  - **`acme_cname_target`**

    `string` — CNAME target for ACME certificate provisioning

  - **`activated_at`**

    `string | null`, format: `date-time`

  - **`created_at`**

    `string`, format: `date-time`

  - **`dns_records`**

    `array` — Required DNS records for domain setup

    **Items:**

    - **`name`**

      `string` — DNS record name

    - **`type`**

      `string` — DNS record type (TXT, CNAME)

    - **`value`**

      `string` — DNS record value

    - **`verified`**

      `boolean` — Whether this record has been verified

  - **`domain`**

    `string` — The custom domain name

  - **`ready_for_qurls`**

    `boolean` — Whether this domain is ready to be used with qURLs (status is verified, provisioning\_tls, or active)

  - **`status`**

    `string`, possible values: `"pending_verification", "verified", "provisioning_tls", "active", "failed"` — Current domain status

  - **`token_expires_at`**

    `string | null`, format: `date-time` — When the verification token expires. Omitted for domains created before token expiry was introduced (those tokens never expire). After expiry, use the regenerate-token endpoint to get a new one.

  - **`verification_token`**

    `string` — TXT record value for DNS verification

  - **`verified_at`**

    `string | null`, format: `date-time`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "domain": "",
    "status": "pending_verification",
    "verification_token": "",
    "token_expires_at": null,
    "acme_cname_target": "",
    "created_at": "",
    "verified_at": null,
    "activated_at": null,
    "ready_for_qurls": true,
    "dns_records": [
      {
        "type": "",
        "name": "",
        "value": "",
        "verified": true
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 Domain already registered

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### List domains

- **Method:** `GET`
- **Path:** `/v1/domains`
- **Tags:** Domains

Lists custom domains for the authenticated user.

#### Responses

##### Status: 200 List of domains

###### Content-Type: application/json

- **`data`**

  `array`

  **Items:**

  - **`acme_cname_target`**

    `string` — CNAME target for ACME certificate provisioning

  - **`activated_at`**

    `string | null`, format: `date-time`

  - **`created_at`**

    `string`, format: `date-time`

  - **`dns_records`**

    `array` — Required DNS records for domain setup

    **Items:**

    - **`name`**

      `string` — DNS record name

    - **`type`**

      `string` — DNS record type (TXT, CNAME)

    - **`value`**

      `string` — DNS record value

    - **`verified`**

      `boolean` — Whether this record has been verified

  - **`domain`**

    `string` — The custom domain name

  - **`ready_for_qurls`**

    `boolean` — Whether this domain is ready to be used with qURLs (status is verified, provisioning\_tls, or active)

  - **`status`**

    `string`, possible values: `"pending_verification", "verified", "provisioning_tls", "active", "failed"` — Current domain status

  - **`token_expires_at`**

    `string | null`, format: `date-time` — When the verification token expires. Omitted for domains created before token expiry was introduced (those tokens never expire). After expiry, use the regenerate-token endpoint to get a new one.

  - **`verification_token`**

    `string` — TXT record value for DNS verification

  - **`verified_at`**

    `string | null`, format: `date-time`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "domain": "",
      "status": "pending_verification",
      "verification_token": "",
      "token_expires_at": null,
      "acme_cname_target": "",
      "created_at": "",
      "verified_at": null,
      "activated_at": null,
      "ready_for_qurls": true,
      "dns_records": [
        {
          "type": "",
          "name": "",
          "value": "",
          "verified": true
        }
      ]
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Get domain status

- **Method:** `GET`
- **Path:** `/v1/domains/{domain}`
- **Tags:** Domains

Retrieves the status and DNS configuration for a custom domain.

#### Responses

##### Status: 200 Domain details

###### Content-Type: application/json

- **`data`**

  `object`

  - **`acme_cname_target`**

    `string` — CNAME target for ACME certificate provisioning

  - **`activated_at`**

    `string | null`, format: `date-time`

  - **`created_at`**

    `string`, format: `date-time`

  - **`dns_records`**

    `array` — Required DNS records for domain setup

    **Items:**

    - **`name`**

      `string` — DNS record name

    - **`type`**

      `string` — DNS record type (TXT, CNAME)

    - **`value`**

      `string` — DNS record value

    - **`verified`**

      `boolean` — Whether this record has been verified

  - **`domain`**

    `string` — The custom domain name

  - **`ready_for_qurls`**

    `boolean` — Whether this domain is ready to be used with qURLs (status is verified, provisioning\_tls, or active)

  - **`status`**

    `string`, possible values: `"pending_verification", "verified", "provisioning_tls", "active", "failed"` — Current domain status

  - **`token_expires_at`**

    `string | null`, format: `date-time` — When the verification token expires. Omitted for domains created before token expiry was introduced (those tokens never expire). After expiry, use the regenerate-token endpoint to get a new one.

  - **`verification_token`**

    `string` — TXT record value for DNS verification

  - **`verified_at`**

    `string | null`, format: `date-time`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "domain": "",
    "status": "pending_verification",
    "verification_token": "",
    "token_expires_at": null,
    "acme_cname_target": "",
    "created_at": "",
    "verified_at": null,
    "activated_at": null,
    "ready_for_qurls": true,
    "dns_records": [
      {
        "type": "",
        "name": "",
        "value": "",
        "verified": true
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Remove domain

- **Method:** `DELETE`
- **Path:** `/v1/domains/{domain}`
- **Tags:** Domains

Removes a custom domain registration.

#### Responses

##### Status: 204 Domain removed successfully

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Trigger DNS verification

- **Method:** `POST`
- **Path:** `/v1/domains/{domain}/verify`
- **Tags:** Domains

Triggers DNS verification for a custom domain. Checks TXT record, ACME CNAME, and traffic routing configuration.

#### Responses

##### Status: 200 Verification result

###### Content-Type: application/json

- **`data`**

  `object`

  - **`checks`**

    `object`

    - **`acme_cname`**

      `object`

      - **`verified` (required)**

        `boolean`

      - **`error`**

        `string` — Error message when the check fails (omitted when verified).

      - **`found`**

        `string` — The actual value found in DNS (omitted when nothing was found).

    - **`traffic_routing`**

      `object`

      - **`verified` (required)**

        `boolean`

      - **`error`**

        `string` — Error message when the check fails (omitted when verified).

      - **`found`**

        `string` — The actual value found in DNS (omitted when nothing was found).

    - **`txt`**

      `object`

      - **`verified` (required)**

        `boolean`

      - **`error`**

        `string` — Error message when the check fails (omitted when verified).

      - **`found`**

        `string` — The actual value found in DNS (omitted when nothing was found).

  - **`domain`**

    `string`

  - **`status`**

    `string`, possible values: `"pending_verification", "verified", "provisioning_tls", "active", "failed"`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "domain": "",
    "status": "pending_verification",
    "checks": {
      "txt": {
        "verified": true,
        "error": "",
        "found": ""
      },
      "acme_cname": {
        "verified": true,
        "error": "",
        "found": ""
      },
      "traffic_routing": {
        "verified": true,
        "error": "",
        "found": ""
      }
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 422 Verification token has expired — use POST /v1/domains/{domain}/regenerate-token to get a new one

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Regenerate verification token

- **Method:** `POST`
- **Path:** `/v1/domains/{domain}/regenerate-token`
- **Tags:** Domains

Regenerates the DNS verification token for a custom domain. The previous token is invalidated immediately. A new TXT record must be configured with the returned token before calling the verify endpoint.

Only allowed for domains in `pending_verification` or `failed` status. The new token expires after 7 days.

#### Responses

##### Status: 200 Token regenerated successfully

###### Content-Type: application/json

- **`data`**

  `object`

  - **`acme_cname_target`**

    `string` — CNAME target for ACME certificate provisioning

  - **`activated_at`**

    `string | null`, format: `date-time`

  - **`created_at`**

    `string`, format: `date-time`

  - **`dns_records`**

    `array` — Required DNS records for domain setup

    **Items:**

    - **`name`**

      `string` — DNS record name

    - **`type`**

      `string` — DNS record type (TXT, CNAME)

    - **`value`**

      `string` — DNS record value

    - **`verified`**

      `boolean` — Whether this record has been verified

  - **`domain`**

    `string` — The custom domain name

  - **`ready_for_qurls`**

    `boolean` — Whether this domain is ready to be used with qURLs (status is verified, provisioning\_tls, or active)

  - **`status`**

    `string`, possible values: `"pending_verification", "verified", "provisioning_tls", "active", "failed"` — Current domain status

  - **`token_expires_at`**

    `string | null`, format: `date-time` — When the verification token expires. Omitted for domains created before token expiry was introduced (those tokens never expire). After expiry, use the regenerate-token endpoint to get a new one.

  - **`verification_token`**

    `string` — TXT record value for DNS verification

  - **`verified_at`**

    `string | null`, format: `date-time`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "domain": "",
    "status": "pending_verification",
    "verification_token": "",
    "token_expires_at": null,
    "acme_cname_target": "",
    "created_at": "",
    "verified_at": null,
    "activated_at": null,
    "ready_for_qurls": true,
    "dns_records": [
      {
        "type": "",
        "name": "",
        "value": "",
        "verified": true
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 Token regeneration not allowed for current domain status

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### List webhooks

- **Method:** `GET`
- **Path:** `/v1/webhooks`
- **Tags:** Webhooks

Lists webhook endpoints owned by the authenticated user.

#### Responses

##### Status: 200 List of webhooks

###### Content-Type: application/json

- **`data`**

  `array`

  **Items:**

  - **`created_at`**

    `string`, format: `date-time`

  - **`description`**

    `string`

  - **`events`**

    `array`

    **Items:**

    `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

  - **`failure_count`**

    `integer` — Consecutive delivery failures

  - **`last_delivery_success`**

    `boolean`

  - **`last_delivery_time`**

    `integer` — Unix timestamp of last delivery attempt

  - **`owner_id`**

    `string` — Identifier of the account that owns this subscription. Bound at create time from the authenticating API key and immutable thereafter.

  - **`status`**

    `string`, possible values: `"active", "disabled"`

  - **`updated_at`**

    `string`, format: `date-time`

  - **`url`**

    `string`, format: `uri`

  - **`webhook_id`**

    `string`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "webhook_id": "",
      "owner_id": "auth0|user123",
      "url": "",
      "events": [
        "qurl.created"
      ],
      "status": "active",
      "description": "",
      "created_at": "",
      "updated_at": "",
      "failure_count": 1,
      "last_delivery_success": true,
      "last_delivery_time": 1
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Create a webhook

- **Method:** `POST`
- **Path:** `/v1/webhooks`
- **Tags:** Webhooks

Creates a new webhook endpoint. The secret is returned only once in the response. Store it securely - it cannot be retrieved later (only regenerated).

#### Request Body

##### Content-Type: application/json

- **`events` (required)**

  `array` — Event types to subscribe to

  **Items:**

  `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

- **`url` (required)**

  `string`, format: `uri` — HTTPS endpoint URL to receive webhook events (max 2048 characters)

- **`description`**

  `string` — Human-readable description

**Example:**

```json
{
  "url": "https://example.com/webhooks/qurl",
  "events": [
    "qurl.created"
  ],
  "description": ""
}
```

#### Responses

##### Status: 201 Webhook created successfully

###### Content-Type: application/json

- **`data`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "owner_id": "auth0|user123",
    "url": "",
    "events": [
      "qurl.created"
    ],
    "status": "active",
    "description": "",
    "created_at": "",
    "updated_at": "",
    "failure_count": 1,
    "last_delivery_success": true,
    "last_delivery_time": 1,
    "secret": "whsec_K8xQp9H2sJ9Lx7R4AaBbCcDdEeFf..."
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### List available event types

- **Method:** `GET`
- **Path:** `/v1/webhooks/events`
- **Tags:** Webhooks

Returns all available webhook event types with descriptions.

#### Responses

##### Status: 200 List of event types

###### Content-Type: application/json

- **`data`**

  `array`

  **Items:**

  - **`category`**

    `string`, possible values: `"resource", "access", "quota", "token"`

  - **`description`**

    `string`

  - **`type`**

    `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": [
    {
      "type": "qurl.created",
      "category": "resource",
      "description": ""
    }
  ],
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Get a webhook

- **Method:** `GET`
- **Path:** `/v1/webhooks/{id}`
- **Tags:** Webhooks

Retrieves a single webhook by ID.

#### Responses

##### Status: 200 Webhook details

###### Content-Type: application/json

- **`data`**

  `object`

  - **`created_at`**

    `string`, format: `date-time`

  - **`description`**

    `string`

  - **`events`**

    `array`

    **Items:**

    `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

  - **`failure_count`**

    `integer` — Consecutive delivery failures

  - **`last_delivery_success`**

    `boolean`

  - **`last_delivery_time`**

    `integer` — Unix timestamp of last delivery attempt

  - **`owner_id`**

    `string` — Identifier of the account that owns this subscription. Bound at create time from the authenticating API key and immutable thereafter.

  - **`status`**

    `string`, possible values: `"active", "disabled"`

  - **`updated_at`**

    `string`, format: `date-time`

  - **`url`**

    `string`, format: `uri`

  - **`webhook_id`**

    `string`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "owner_id": "auth0|user123",
    "url": "",
    "events": [
      "qurl.created"
    ],
    "status": "active",
    "description": "",
    "created_at": "",
    "updated_at": "",
    "failure_count": 1,
    "last_delivery_success": true,
    "last_delivery_time": 1
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Update a webhook

- **Method:** `PATCH`
- **Path:** `/v1/webhooks/{id}`
- **Tags:** Webhooks

Updates a webhook's configuration.

#### Request Body

##### Content-Type: application/json

- **`description`**

  `string`

- **`events`**

  `array`

  **Items:**

  `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

- **`status`**

  `string`, possible values: `"active", "disabled"`

- **`url`**

  `string`, format: `uri`

**Example:**

```json
{
  "url": "",
  "events": [
    "qurl.created"
  ],
  "description": "",
  "status": "active"
}
```

#### Responses

##### Status: 200 Webhook updated successfully

###### Content-Type: application/json

- **`data`**

  `object`

  - **`created_at`**

    `string`, format: `date-time`

  - **`description`**

    `string`

  - **`events`**

    `array`

    **Items:**

    `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

  - **`failure_count`**

    `integer` — Consecutive delivery failures

  - **`last_delivery_success`**

    `boolean`

  - **`last_delivery_time`**

    `integer` — Unix timestamp of last delivery attempt

  - **`owner_id`**

    `string` — Identifier of the account that owns this subscription. Bound at create time from the authenticating API key and immutable thereafter.

  - **`status`**

    `string`, possible values: `"active", "disabled"`

  - **`updated_at`**

    `string`, format: `date-time`

  - **`url`**

    `string`, format: `uri`

  - **`webhook_id`**

    `string`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "owner_id": "auth0|user123",
    "url": "",
    "events": [
      "qurl.created"
    ],
    "status": "active",
    "description": "",
    "created_at": "",
    "updated_at": "",
    "failure_count": 1,
    "last_delivery_success": true,
    "last_delivery_time": 1
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Delete a webhook

- **Method:** `DELETE`
- **Path:** `/v1/webhooks/{id}`
- **Tags:** Webhooks

Deletes a webhook endpoint.

#### Responses

##### Status: 204 Webhook deleted successfully

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Regenerate webhook secret

- **Method:** `POST`
- **Path:** `/v1/webhooks/{id}/secret`
- **Tags:** Webhooks

Regenerates the signing secret for a webhook. The old secret is immediately invalidated. The new secret is returned only once - store it securely.

#### Responses

##### Status: 200 Secret regenerated successfully

###### Content-Type: application/json

- **`data`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "owner_id": "auth0|user123",
    "url": "",
    "events": [
      "qurl.created"
    ],
    "status": "active",
    "description": "",
    "created_at": "",
    "updated_at": "",
    "failure_count": 1,
    "last_delivery_success": true,
    "last_delivery_time": 1,
    "secret": "whsec_K8xQp9H2sJ9Lx7R4AaBbCcDdEeFf..."
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### List webhook deliveries

- **Method:** `GET`
- **Path:** `/v1/webhooks/{id}/deliveries`
- **Tags:** Webhooks

Lists recent delivery attempts for a webhook.

#### Responses

##### Status: 200 List of delivery attempts

###### Content-Type: application/json

- **`data`**

  `array`

  **Items:**

  - **`completed_at`**

    `string`, format: `date-time` — Time the most recent delivery attempt completed, including an attempt that will be retried.

  - **`created_at`**

    `string`, format: `date-time`

  - **`delivery_id`**

    `string`

  - **`duration_ms`**

    `integer`

  - **`error_message`**

    `string`

  - **`event_type`**

    `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

  - **`response_body`**

    `string` — Response body limited to 4096 bytes plus a truncation marker.

  - **`response_code`**

    `integer`

  - **`retry_count`**

    `integer` — Persisted attempt claims, including the first attempt. Older delivery rows counted failed attempts.

  - **`status`**

    `string`, possible values: `"pending", "success", "failed", "retrying", "abandoned"` — Retrying includes durable deliveries waiting for their first attempt or held by an active attempt lease.

  - **`webhook_id`**

    `string`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "delivery_id": "",
      "webhook_id": "",
      "event_type": "qurl.created",
      "status": "pending",
      "response_code": 1,
      "response_body": "",
      "error_message": "",
      "duration_ms": 1,
      "retry_count": 1,
      "created_at": "",
      "completed_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/api-keys

- **Method:** `PARAMETERS`
- **Path:** `/v1/api-keys`

### Create an API key or enrollment token

- **Method:** `POST`
- **Path:** `/v1/api-keys`
- **Tags:** API Keys

Creates an account API key or a one-use enrollment token. Save the returned secret; GET and list operations do not return it. See `kind` for enrollment and recovery rules.

| Kind               | Supply                                                           | Purpose                        |
| ------------------ | ---------------------------------------------------------------- | ------------------------------ |
| `api_key`          | `name`, explicit `scopes`                                        | Account API access.            |
| `enrollment_token` | `name`, optional `target`, `claims`, `expires_in`; omit `scopes` | Enroll one agent or Connector. |
| `device`           | Not accepted here                                                | Created during enrollment.     |

**Permissions**

| Action                     | Required credential and scopes                                                                           |
| -------------------------- | -------------------------------------------------------------------------------------------------------- |
| Create an enrollment token | JWT or account API key with `qurl:agent`.                                                                |
| Create an account key      | JWT with `qurl:write`. To grant `qurl:agent` or `qurl:keys`, the JWT must also hold that scope.          |
| Create a child account key | Account API key with `qurl:keys` and either `qurl:write` or `qurl:agent`; an unlimited plan is required. |

Child-key scopes must be a subset of the parent's and cannot include `qurl:keys`. Child keys expire within 24 hours and no later than their parent. Parent revocation does not revoke children or enrolled devices; revoke them separately. Automation callers can list, get, and revoke same-owner keys, but cannot update them.

Enrollment tokens cannot create credentials. A device credential can create only a Connector enrollment token within its permitted binding.

**Retries:** Supply `Idempotency-Key`. For 24 hours, the same key and body recover the original response, including its secret. A different body returns `409`. Clients must tolerate future credential `kind` values.

#### Request Body

##### Content-Type: application/json

- **`kind` (required)**

  `string`, possible values: `"api_key", "enrollment_token"` — Choose \`api\_key\` for account API access or \`enrollment\_token\` to enroll one device. \`device\` credentials are created by enrollment and cannot be created here. An enrollment token lasts at most 24 hours. Its scopes come from \`target\`. Connector enrollment requires one Connector claim; agent enrollment can omit claims or use one Connector claim. See \`target\` and \`claims\` for the accepted fields. Account setup without a browser requires an emailed verification code before account-key creation; using an enrollment token does not require another emailed code. Use a token for one enrollment at a time. Success consumes it, including finding an existing bound Connector. Validation, conflict, and temporary failures before the action do not consume it. After \`500\`, retry the same enrollment. If the retry returns \`401\`, obtain a new token and check the resource with an account credential. On \`409 bootstrap\_key\_consumed\`, the action can have partly succeeded; use the same recovery.

- **`name` (required)**

  `string` — Human-readable name for the credential

- **`claims`**

  `array` — Resource claims binding a \`kind=enrollment\_token\` credential. At most ONE claim entry is supported today; more return 400 \`invalid\_input\`. A bound token may only enroll against the claimed resource. Not accepted for \`kind=api\_key\` (400 \`invalid\_input\`).

  **Items:**

  - **`id` (required)**

    `string` — The claimed resource id. For \`type=connector\` this is the connector id (3-64 chars, lowercase a-z0-9 with hyphens, starting with a letter and ending alphanumeric). An id that does not match returns 400 \`invalid\_input\`.

  - **`type` (required)**

    `string`, possible values: `"connector"` — The claimed resource type. \`connector\` is the only claim type today; future claim types may be added.

- **`expires_in`**

  `string` — Optional lifetime for \`kind=enrollment\_token\` credentials. Defaults to 24h and cannot exceed 24h. Invalid format or bounds return 400 \`invalid\_duration\`. Not accepted for \`kind=api\_key\` (400 \`invalid\_input\`): API keys are durable.

- **`scopes`**

  `array` — Permissions granted to a \`kind=api\_key\` credential. Required for \`kind=api\_key\`. Requesting \`qurl:agent\` requires the JWT caller to hold \`qurl:agent\`. Must be OMITTED for \`kind=enrollment\_token\`: token scopes are server-assigned from \`target\` (agent -> \`qurl:agent\`; connector -> \`qurl:agent\` + \`qurl:write\`) and sending this field returns 400 \`invalid\_input\`.

  **Items:**

  `string`, possible values: `"qurl:read", "qurl:write", "qurl:resolve", "qurl:agent", "qurl:keys"`

- **`target`**

  `string`, possible values: `"agent", "connector"` — What a \`kind=enrollment\_token\` credential enrolls. When omitted it is derived from \`claims\`: a connector claim implies \`connector\`, otherwise \`agent\`. \`target=connector\` requires exactly one claim of type \`connector\` (the connector id the token is bound to); \`target=agent\` accepts zero claims (unbound) or one connector claim (a bound agent token). Not accepted for \`kind=api\_key\` (400 \`invalid\_input\`).

**Example:**

```json
{
  "kind": "api_key",
  "name": "",
  "scopes": [
    "qurl:read"
  ],
  "target": "agent",
  "claims": [
    {
      "type": "connector",
      "id": ""
    }
  ],
  "expires_in": ""
}
```

#### Responses

##### Status: 201 Credential created successfully.

###### Content-Type: application/json

- **`data`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "key_id": "",
    "key_prefix": "",
    "kind": "api_key",
    "target": "agent",
    "claims": [
      {
        "type": "connector",
        "id": ""
      }
    ],
    "name": "",
    "scopes": [
      ""
    ],
    "status": "active",
    "created_at": "",
    "updated_at": "",
    "last_used_at": "",
    "expires_at": "",
    "api_key": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Forbidden. Either: - \`kind=enrollment\_token\` was requested without \`qurl:agent\`, or with a one-shot credential, or - API key auth without \`qurl:keys\` was used for \`kind=api\_key\`, or - Child scopes exceed the parent or include \`qurl:keys\` (\`scope\_delegation\_denied\`), or - A JWT without \`qurl:write\` requested \`kind=api\_key\`, or - A JWT requested a durable key whose \`scopes\` include \`qurl:agent\` but the JWT does not hold \`qurl:agent\`. Error code: \`scope\_delegation\_denied\`, or - The authenticated JWT email is not verified. Error code: \`email\_verification\_required\`, or - The account is frozen. Error code: \`account\_frozen\`, or - Plan key limit reached (free: 3, growth: 50). Error code: \`api\_key\_limit\`.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 The supplied \`Idempotency-Key\` was previously used with a different request body. Send the original body or use a fresh key.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 A transient failure prevented safe idempotency recovery or account identity verification. Retry after the supplied delay; when an \`Idempotency-Key\` was supplied, reuse the same key and request body.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### List API keys

- **Method:** `GET`
- **Path:** `/v1/api-keys`
- **Tags:** API Keys

Lists credentials owned by the authenticated user — durable API keys (`kind=api_key`), one-shot enrollment tokens (`kind=enrollment_token`, with `target` and any bound `claims`), and system-minted device credentials (`kind=device`). Unknown future `kind` values may appear; clients must tolerate them. The plaintext secret is never returned in list responses. Requires JWT authentication or an ordinary account API key with `qurl:keys` and the route scope. Only the authenticated owner's keys are accessible. Results are ordered by key creation time, newest first, including `status=revoked`; older retained revoked keys can require additional pages or direct lookup by `key_id`.

#### Responses

##### Status: 200 List of API keys

###### Content-Type: application/json

- **`data`**

  `array`

  **Items:**

  - **`claims`**

    `array` — Resource claims bound to a \`kind=enrollment\_token\` credential. Present only on bound enrollment tokens (always exactly one entry today).

    **Items:**

    - **`id` (required)**

      `string` — The claimed resource id. For \`type=connector\` this is the connector id (3-64 chars, lowercase a-z0-9 with hyphens, starting with a letter and ending alphanumeric). An id that does not match returns 400 \`invalid\_input\`.

    - **`type` (required)**

      `string`, possible values: `"connector"` — The claimed resource type. \`connector\` is the only claim type today; future claim types may be added.

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string`, format: `date-time` — Expiration timestamp for enrollment tokens and other expiring credentials. Omitted for non-expiring credentials.

  - **`key_id`**

    `string`

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

  - **`kind`**

    `string`, possible values: `"api_key", "enrollment_token", "device"` — The credential kind: a durable \`api\_key\`, a one-shot \`enrollment\_token\` (see \`target\` and \`claims\`), or a system-minted per-device \`device\` credential. Unknown future kind values may appear as the credential model grows; clients must tolerate values outside this enum.

  - **`last_used_at`**

    `string`, format: `date-time`

  - **`name`**

    `string`

  - **`scopes`**

    `array` — Permissions granted to this credential (server-assigned for enrollment tokens and device credentials). Returned in alphabetical order for deterministic responses.

    **Items:**

    `string`

  - **`status`**

    `string`, possible values: `"active", "revoked"`

  - **`target`**

    `string`, possible values: `"agent", "connector"` — What a \`kind=enrollment\_token\` credential enrolls. Present only on enrollment tokens.

  - **`updated_at`**

    `string`, format: `date-time` — Timestamp of the last update to name or scopes. Omitted if the key has never been updated.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "key_id": "",
      "key_prefix": "",
      "kind": "api_key",
      "target": "agent",
      "claims": [
        {
          "type": "connector",
          "id": ""
        }
      ],
      "name": "",
      "scopes": [
        ""
      ],
      "status": "active",
      "created_at": "",
      "updated_at": "",
      "last_used_at": "",
      "expires_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PARAMETERS /v1/api-keys/{key\_id}

- **Method:** `PARAMETERS`
- **Path:** `/v1/api-keys/{key_id}`

### Get a credential

- **Method:** `GET`
- **Path:** `/v1/api-keys/{key_id}`
- **Tags:** API Keys

Returns a credential owned by your account, including retained revoked credentials. The plaintext secret is never returned. Unknown keys and another account's keys return `404`. Requires JWT authentication, or an ordinary account API key with `qurl:keys` and the operation's scope.

Check both `status` and `expires_at` before treating a key as usable. Unlike the list operation, this lookup does not filter expired active keys. A recent change can take time to appear. Retry if you are confirming a recent revocation.

#### Responses

##### Status: 200 API key found

###### Content-Type: application/json

- **`data`**

  `object`

  - **`claims`**

    `array` — Resource claims bound to a \`kind=enrollment\_token\` credential. Present only on bound enrollment tokens (always exactly one entry today).

    **Items:**

    - **`id` (required)**

      `string` — The claimed resource id. For \`type=connector\` this is the connector id (3-64 chars, lowercase a-z0-9 with hyphens, starting with a letter and ending alphanumeric). An id that does not match returns 400 \`invalid\_input\`.

    - **`type` (required)**

      `string`, possible values: `"connector"` — The claimed resource type. \`connector\` is the only claim type today; future claim types may be added.

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string`, format: `date-time` — Expiration timestamp for enrollment tokens and other expiring credentials. Omitted for non-expiring credentials.

  - **`key_id`**

    `string`

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

  - **`kind`**

    `string`, possible values: `"api_key", "enrollment_token", "device"` — The credential kind: a durable \`api\_key\`, a one-shot \`enrollment\_token\` (see \`target\` and \`claims\`), or a system-minted per-device \`device\` credential. Unknown future kind values may appear as the credential model grows; clients must tolerate values outside this enum.

  - **`last_used_at`**

    `string`, format: `date-time`

  - **`name`**

    `string`

  - **`scopes`**

    `array` — Permissions granted to this credential (server-assigned for enrollment tokens and device credentials). Returned in alphabetical order for deterministic responses.

    **Items:**

    `string`

  - **`status`**

    `string`, possible values: `"active", "revoked"`

  - **`target`**

    `string`, possible values: `"agent", "connector"` — What a \`kind=enrollment\_token\` credential enrolls. Present only on enrollment tokens.

  - **`updated_at`**

    `string`, format: `date-time` — Timestamp of the last update to name or scopes. Omitted if the key has never been updated.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "key_id": "",
    "key_prefix": "",
    "kind": "api_key",
    "target": "agent",
    "claims": [
      {
        "type": "connector",
        "id": ""
      }
    ],
    "name": "",
    "scopes": [
      ""
    ],
    "status": "active",
    "created_at": "",
    "updated_at": "",
    "last_used_at": "",
    "expires_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Update an API key

- **Method:** `PATCH`
- **Path:** `/v1/api-keys/{key_id}`
- **Tags:** API Keys

Updates the name or scopes of an existing credential. Requires JWT authentication (API keys cannot update API keys). The caller must hold `qurl:agent` when the replacement `scopes` add `qurl:agent` to a key that does not already hold it. Resending a full scope set that preserves an existing `qurl:agent`, or removing or narrowing scopes, requires no additional permission beyond `qurl:write`. `scopes` are updatable only on `kind=api_key` credentials: an enrollment token's scopes are server-assigned (400 `invalid_input`), and device credentials cannot be edited at all (400 `invalid_input`). The scope-delegation 403 is evaluated before kind editability, so an under-scoped attempt to add `qurl:agent` to a device credential returns 403 rather than the 400 an authorized caller would see. A credential's `kind` can never change. If neither name nor scopes are provided, the existing key is returned unchanged (no-op). Any replacement scope set that includes `qurl:keys` requires the JWT to hold `qurl:keys`, even if the key already has it.

#### Request Body

##### Content-Type: application/json

- **`name`**

  `string` — Updated name for the credential

- **`scopes`**

  `array` — Updated permissions for the credential. Only \`kind=api\_key\` credentials accept scope updates: enrollment-token scopes are server-assigned and device credentials cannot be edited (both return 400 \`invalid\_input\`). Adding \`qurl:agent\` to a key that does not already hold it requires the JWT caller to hold \`qurl:agent\`. Keeping or removing an existing \`qurl:agent\` scope requires no additional permission beyond \`qurl:write\`. Any replacement set that includes \`qurl:keys\` requires the JWT to hold \`qurl:keys\`, even when that scope is already on the key. Omit this field to leave scopes unchanged. When provided, replaces all existing scopes (not a merge).

  **Items:**

  `string`, possible values: `"qurl:read", "qurl:write", "qurl:resolve", "qurl:agent", "qurl:keys"`

**Example:**

```json
{
  "name": "",
  "scopes": [
    "qurl:read"
  ]
}
```

#### Responses

##### Status: 200 API key updated successfully

###### Content-Type: application/json

- **`data`**

  `object`

  - **`claims`**

    `array` — Resource claims bound to a \`kind=enrollment\_token\` credential. Present only on bound enrollment tokens (always exactly one entry today).

    **Items:**

    - **`id` (required)**

      `string` — The claimed resource id. For \`type=connector\` this is the connector id (3-64 chars, lowercase a-z0-9 with hyphens, starting with a letter and ending alphanumeric). An id that does not match returns 400 \`invalid\_input\`.

    - **`type` (required)**

      `string`, possible values: `"connector"` — The claimed resource type. \`connector\` is the only claim type today; future claim types may be added.

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string`, format: `date-time` — Expiration timestamp for enrollment tokens and other expiring credentials. Omitted for non-expiring credentials.

  - **`key_id`**

    `string`

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

  - **`kind`**

    `string`, possible values: `"api_key", "enrollment_token", "device"` — The credential kind: a durable \`api\_key\`, a one-shot \`enrollment\_token\` (see \`target\` and \`claims\`), or a system-minted per-device \`device\` credential. Unknown future kind values may appear as the credential model grows; clients must tolerate values outside this enum.

  - **`last_used_at`**

    `string`, format: `date-time`

  - **`name`**

    `string`

  - **`scopes`**

    `array` — Permissions granted to this credential (server-assigned for enrollment tokens and device credentials). Returned in alphabetical order for deterministic responses.

    **Items:**

    `string`

  - **`status`**

    `string`, possible values: `"active", "revoked"`

  - **`target`**

    `string`, possible values: `"agent", "connector"` — What a \`kind=enrollment\_token\` credential enrolls. Present only on enrollment tokens.

  - **`updated_at`**

    `string`, format: `date-time` — Timestamp of the last update to name or scopes. Omitted if the key has never been updated.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "key_id": "",
    "key_prefix": "",
    "kind": "api_key",
    "target": "agent",
    "claims": [
      {
        "type": "connector",
        "id": ""
      }
    ],
    "name": "",
    "scopes": [
      ""
    ],
    "status": "active",
    "created_at": "",
    "updated_at": "",
    "last_used_at": "",
    "expires_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Forbidden. Either: - API key authentication was used (JWT required), or - The replacement scope set adds \`qurl:agent\` but the JWT does not hold \`qurl:agent\`. Error code: \`scope\_delegation\_denied\`, or - The account is frozen. Error code: \`account\_frozen\`.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 The account identity could not be verified before the update. No API-key mutation was committed; retry after the supplied delay.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Revoke a credential

- **Method:** `DELETE`
- **Path:** `/v1/api-keys/{key_id}`
- **Tags:** API Keys

Revokes a credential owned by your account. Revoked records are retained for 30 days. Requires JWT authentication, or an ordinary account API key with `qurl:keys` and the operation's scope. Revoking a parent key does not revoke child keys or device credentials; revoke those separately.

#### Responses

##### Status: 204 API key revoked successfully

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Create or rotate an external identity binding

- **Method:** `POST`
- **Path:** `/v1/external-identity-bindings`
- **Tags:** Bindings

Links an external identity to your account and returns its API key. Requires a JWT issued to the trusted OAuth client for the provider. Missing, conflicting, or untrusted `azp` or `client_id` claims return `403`. Discord, Slack, and Teams each require their own configured trusted client. An unconfigured provider returns `403`.

The binding and key are created together or not at all. The key counts toward your account's API-key limit. Each provider identity can belong to one account; another account's binding returns `409`. Its owner must release it first.

**Rotation:** Set `rotate_existing: true` only for an explicit install or reauthorization. This revokes the old key and returns a new key with the same `binding_id`, without using another key slot. Do not rotate during routine startup. Run one install or reauthorization at a time per identity: concurrent rotations with different idempotency keys can revoke each other's new keys. A `503` can mean another rotation succeeded; retrying can replace that rotation's key.

**Scopes:** Slack and Teams keys receive `qurl:read`, `qurl:write`, and `qurl:agent`; for those providers the JWT must also hold `qurl:agent`. Discord keys receive `qurl:read` and `qurl:write`. Do not send `scopes`; the provider determines them.

**Retries:** `Idempotency-Key` is required. Recover a lost response within 24 hours with the same key and body. A different body returns `409 idempotency_conflict`. On `503 service_unavailable`, wait for `Retry-After` and retry the same request. On `503 bindings_disabled`, use your fallback flow; retries will not enable the operation. See the `409` response for recovery after the replay window or concurrent rotation.

#### Request Body

##### Content-Type: application/json

- **`external_id` (required)**

  `string` — Provider-specific stable identifier (Slack \`team\_id\`, Discord \`guild\_id\`, or the canonical Microsoft Entra tenant ID for Teams). qURL treats the value as opaque; canonicalization is the trusted provider flow's responsibility. Only visible ASCII is allowed, and \`#\` is not allowed.

- **`provider` (required)**

  `string`, possible values: `"slack", "discord", "teams"` — Provider that owns the external identity: Slack, Discord, or Microsoft Teams. Unknown values fail request validation. Every provider requires its own configured confidential OAuth client; a request whose verified token presents a different client, or whose provider is not configured, fails with 403.

- **`display_name`**

  `string` — Optional human-readable label for audit/display. An explicit rotation that omits this field preserves the current label.

- **`rotate_existing`**

  `boolean`, default: `false` — Explicitly allow this request to revoke and replace a same-owner binding credential. Omit or set false for first-install-only behavior.

**Example:**

```json
{
  "provider": "slack",
  "external_id": "",
  "display_name": "",
  "rotate_existing": false
}
```

#### Responses

##### Status: 201 Binding and API key created or rotated atomically. Rotation revokes the API key that was previously issued for this binding.

###### Content-Type: application/json

- **`api_key`**

  `object`

  - **`key_id`**

    `string`

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification.

  - **`plaintext`**

    `string` — The plaintext API key. Returned only once at creation time — store it securely.

- **`binding_id`**

  `string` — Unique binding identifier.

- **`created_at`**

  `string`, format: `date-time`

- **`display_name`**

  `string`

- **`external_id`**

  `string` — Opaque provider-specific stable identifier.

- **`provider`**

  `string` — Provider that owns the external identity.

- **`scopes`**

  `array` — Scopes the minted API key was issued with.

  **Items:**

  `string`

**Example:**

```json
{
  "binding_id": "",
  "provider": "",
  "external_id": "",
  "display_name": "",
  "api_key": {
    "key_id": "",
    "key_prefix": "",
    "plaintext": ""
  },
  "scopes": [
    ""
  ],
  "created_at": ""
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Forbidden. Either: - The authenticated JWT email is not verified. Error code: \`email\_verification\_required\`, or - The JWT was not issued to the configured OAuth client for the requested provider. Error code: \`forbidden\`, or - The provider-assigned scopes include \`qurl:agent\` but the JWT caller does not hold \`qurl:agent\`. Error code: \`scope\_delegation\_denied\`, or - The account is frozen. Error code: \`account\_frozen\`, or - The authenticated owner has reached the API-key plan limit shared with \`POST /v1/api-keys\`. Error code: \`api\_key\_limit\`.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 409 Either (a) the provider identity is already bound to another owner, (b) the authenticated owner already holds the binding and \`rotate\_existing\` was omitted or false, or (c) the supplied \`Idempotency-Key\` was previously used with a different request body. The conditions surface two distinct error codes: \`already\_exists\` for (a) and (b), and \`idempotency\_conflict\` for (c), so clients can handle them separately. Retry note: a prior \`201\` whose response never reached the client should be retried within 24 hours with the SAME \`Idempotency-Key\` and the SAME body — the original response replays. A later explicit reauthorization with a NEW \`Idempotency-Key\` and \`rotate\_existing: true\` may safely rotate an unseen prior commit and converge on one active binding generation. After the original replay window, its plaintext key cannot be retrieved. A successful DELETE ends that replay window; a later POST with the same key creates a new binding. If idempotency recovery is temporarily unavailable, the service returns \`503 service\_unavailable\` with \`Retry-After\`; retry with the same key and body.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 422 Semantic validation failed after request-shape validation. Malformed bodies and schema-level request errors return 400.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 The endpoint is unavailable in this environment, or a transient failure prevented safe idempotent replay.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Delete an external identity binding

- **Method:** `DELETE`
- **Path:** `/v1/external-identity-bindings/{binding_id}`
- **Tags:** Bindings

Releases an external provider identity and revokes the API key minted for that binding in one atomic operation. Requires JWT authentication; API keys cannot manage bindings. Malformed or unknown binding IDs and bindings owned by another account all return the same 404.

#### Responses

##### Status: 204 Binding released and bound API key revoked successfully

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 The binding service is switched off, or a required backing store is temporarily unavailable.

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Redeem an access code

- **Method:** `POST`
- **Path:** `/v1/access-codes/redeem`
- **Tags:** Access Codes

Redeems an access code to obtain a time-limited access link to a protected resource. This endpoint is public and does not require authentication.

The response contains a redirect URL. The client should redirect the user to this URL to complete resource access.

Bot protection is applied: submissions with a non-empty honeypot field or elapsed time under 3 seconds receive a fake success response.

A code that cannot be redeemed returns `400 invalid_request`, and `detail` says why in words for the person who holds the code: the code is unknown, expired, revoked or used up, or it is not available. "No longer available" is about the resource that the code leads to: it is closed, and only its owner can change that. "Not currently available" means that no link can be made for the code now: the same code can work at a later time.

#### Request Body

##### Content-Type: application/json

- **`code` (required)**

  `string` — The plaintext access code to redeem

- **`elapsed_ms`**

  `integer` — Milliseconds elapsed since page load (for bot detection)

- **`honeypot`**

  `string`, default: `""` — Honeypot field for bot detection (must be empty)

**Example:**

```json
{
  "code": "ac_k8xqp9h2sj9lx7r4abcdef",
  "honeypot": "",
  "elapsed_ms": 5200
}
```

#### Responses

##### Status: 200 Access code redeemed successfully

###### Content-Type: application/json

- **`data`**

  `object`

  - **`redirect_url`**

    `string`, format: `uri` — URL to redirect the user to for resource access

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "redirect_url": "https://qurl.link/#at_k8xqp9h2sj9lx7r4abcdef"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 503 Upstream service temporarily unavailable (e.g. Stripe rate limit)

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Create an access code

- **Method:** `POST`
- **Path:** `/v1/access-codes`
- **Tags:** Access Codes

Creates a new access code for a protected resource identified by its CRID. The plaintext code is returned only once in the response - store it securely.

Requires system-tier account. The resource must exist and be owned by the authenticated user.

#### Request Body

##### Content-Type: application/json

- **`resource_id` (required)**

  `string` — CRID of the resource to share. The field name remains \`resource\_id\`; a public key is also accepted.

- **`expires_at`**

  `string`, format: `date-time` — Optional expiration time for the access code

- **`max_uses`**

  `integer`, default: `0` — Maximum number of times this code can be redeemed (0 = unlimited)

- **`name`**

  `string` — Human-readable label for the access code

**Example:**

```json
{
  "resource_id": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
  "name": "Investor Stats Q1",
  "max_uses": 10,
  "expires_at": ""
}
```

#### Responses

##### Status: 201 Access code created successfully

###### Content-Type: application/json

- **`data`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "access_code_id": "acd_k8xqp9h2sj9",
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "name": "Investor Stats Q1",
    "status": "active",
    "max_uses": 10,
    "use_count": 3,
    "created_at": "",
    "expires_at": null,
    "code": "ac_k8xqp9h2sj9lx7r4abcdef"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### List access codes

- **Method:** `GET`
- **Path:** `/v1/access-codes`
- **Tags:** Access Codes

Lists access codes owned by the authenticated user, sorted by creation date (newest first). Requires system-tier account.

#### Responses

##### Status: 200 List of access codes

###### Content-Type: application/json

- **`data`**

  `array`

  **Items:**

  - **`access_code_id`**

    `string` — Unique identifier for the access code

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string | null`, format: `date-time`

  - **`max_uses`**

    `integer` — Maximum redemptions allowed (0 = unlimited)

  - **`name`**

    `string` — Human-readable label

  - **`resource_id`**

    `string` — Public key of the resource this code grants access to.

  - **`status`**

    `string`, possible values: `"active", "revoked"` — Current status of the access code

  - **`use_count`**

    `integer` — Number of times this code has been redeemed

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": [
    {
      "access_code_id": "acd_k8xqp9h2sj9",
      "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
      "name": "Investor Stats Q1",
      "status": "active",
      "max_uses": 10,
      "use_count": 3,
      "created_at": "",
      "expires_at": null
    }
  ],
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Revoke an access code

- **Method:** `DELETE`
- **Path:** `/v1/access-codes/{id}`
- **Tags:** Access Codes

Revokes an access code, preventing further redemptions. Requires system-tier account. The code must be owned by the authenticated user.

#### Responses

##### Status: 204 Access code revoked successfully

##### Status: 400 Invalid request parameters

###### Content-Type: application/problem+json

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 403 Insufficient permissions or quota exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 429 Rate limit exceeded

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

##### Status: 500 Internal server error

###### Content-Type: application/problem+json

- **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

## Schemas

### DeviceRecoveryTokenResponse

- **Type:**`object`

* **`data` (required)**

  `object`

  - **`expires_at` (required)**

    `string`, format: `date-time` — Deadline for starting recovery with the retained secret.

  - **`recovery_token_id` (required)**

    `string` — Identifier of the recovery authorization, not the recovery secret.

**Example:**

```json
{
  "data": {
    "recovery_token_id": "",
    "expires_at": ""
  }
}
```

### CreateQurlRequest

- **Type:**`object`

* **`access_policy`**

  `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

  - **`ai_agent_policy`**

    `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

    - **`allow_categories`**

      `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`block_all`**

      `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

    - **`deny_categories`**

      `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

  - **`geo_allowlist`**

    `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`geo_denylist`**

    `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`ip_allowlist`**

    `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`ip_denylist`**

    `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`user_agent_allow_regex`**

    `string` — Regular expression to allow matching user agents (max 256 characters)

  - **`user_agent_deny_regex`**

    `string` — Regular expression to deny matching user agents (max 256 characters)

* **`custom_domain`**

  `string` — Optional custom domain to assign to the auto-created resource. The domain must be registered, active, and owned by the caller. Only allowed when the target URL creates a new resource — rejected if a resource already exists for this target URL.

* **`expires_in`**

  `string` — How long the new link remains valid. Examples: \`5m\`, \`1h\`, \`7d\`, \`1w\`. Minimum: 1 minute. Maximum: 30 days on all plans. The resource's expiration can shorten the link's lifetime; read \`expires\_at\` in the response. When omitted or empty, qURL Connector links use 5 minutes. URL and integration-owned upload links use the service default. Set this field explicitly for a predictable lifetime.

* **`label`**

  `string` — Human-readable label identifying who this qURL is for

* **`max_sessions`**

  `integer` — Maximum concurrent sessions for this qURL, not the whole resource. Use \`0\` for no limit or \`1\`–\`1000\` for a limit. Set \`one\_time\_use: false\` explicitly when you need multiple visitors: some resource types otherwise create a single-visitor link even when \`max\_sessions\` is \`0\`. Visitors that share a source IP count as one session and share its IP policy checks.

* **`one_time_use`**

  `boolean` — Whether the link is consumed by its first successful use. Explicit \`true\` or \`false\` takes precedence over the resource defaults. When omitted, URL and integration-owned upload links use \`false\`. qURL Connector links use \`true\` unless \`max\_sessions\` is greater than 1. For a reusable Connector link, explicitly send \`one\_time\_use: false\`, including when \`max\_sessions\` is \`0\` (unlimited).

* **`private`**

  `boolean` — Privacy of the resource, used only when this request creates it. When omitted, a new resource is private. Send \`false\` to create a public resource. Privacy cannot change after creation. When the request reuses your existing resource for the same \`target\_url\`, omit this field: the resource keeps the privacy it has. A value that differs from the existing resource returns \`400 privacy\_mismatch\`.

* **`session_duration`**

  `string` — How long a session permits new requests after the recipient first reaches the content. This is separate from \`expires\_in\`, which controls the link's expiration. \*\*Default and limits:\*\* Supports seconds (\`30s\`), minutes (\`5m\`), hours (\`1h\`), and days (\`1d\`). Minimum: 1 second. Maximum: 24 hours. If omitted, uses the resource cap when set. Without a cap, qURL Connectors use 1 hour; other resource types use the service default. Set this field explicitly when timing matters. \*\*Resource cap:\*\* On the first create for a target URL, an explicit value also sets the resource's fixed \`session\_duration\_cap\`. Later creates, mints, and updates cannot exceed it (\`400 invalid\_session\_duration\`). See \`ResourceData.session\_duration\_cap\` for how to create a resource with a different cap. \*\*Timing precision:\*\* For signed browser qURLs with a configured duration, the first content request must be authorized within 10 seconds of the start of admission, including time spent completing admission. The session timer then runs for \`session\_duration\` from that first authorization. Initial network admission lasts \`session\_duration + 10 seconds\`; it does not double for long sessions. A 24-hour session gets 24 hours plus 10 seconds of initial network admission. Opening the original invitation again is not required during a live session, including after a TCP connection becomes idle. When duration is omitted on an uncapped URL resource, the existing default authorization deadline is 24 hours plus 10 seconds from admission; the configured-duration first-arrival rule does not apply. New requests are denied at session expiry. Active requests, streams, and WebSockets are canceled at that deadline. The network allowance does not extend application authorization. A single-use invitation remains consumed if the first content request misses the 10-second arrival deadline. Cancellation cannot retract data already delivered or buffered by the client.

* **`target_url`**

  `string`, format: `uri` — The URL to protect with qURL (max 2048 characters). Required for standard reverse-proxy qURLs.

* **`type`**

  `string`, default: `"url"` — Type of resource the qURL grants access to. Defaults to \`url\`; other values are restricted to specific integrations. See \`ResourceType\`.

**Example:**

```json
{
  "type": "url",
  "private": true,
  "target_url": "https://dashboard.example.com",
  "expires_in": "1h",
  "one_time_use": true,
  "max_sessions": 0,
  "session_duration": "1h",
  "access_policy": {
    "ip_allowlist": [
      "192.168.1.0/24",
      "10.0.0.1"
    ],
    "ip_denylist": [
      ""
    ],
    "geo_allowlist": [
      "US",
      "CA",
      "GB"
    ],
    "geo_denylist": [
      "CN",
      "RU"
    ],
    "user_agent_allow_regex": "^Mozilla.*",
    "user_agent_deny_regex": ".*bot.*",
    "ai_agent_policy": {
      "block_all": false,
      "deny_categories": [
        "gptbot",
        "commoncrawl",
        "bytedance"
      ],
      "allow_categories": [
        "claude",
        "chatgpt",
        "perplexity"
      ]
    }
  },
  "label": "Alice from Acme",
  "custom_domain": "app.example.com"
}
```

### QurlMintTargetPath

- **Type:**`string`

Destination path and optional query for a qURL Connector link. For example, `/view/abc?download=1` redirects to that path on the resource's `qurl.site` host. Omit this field to use the host's root. Only qURL Connector resources (`type=tunnel`) accept it. Maximum: 2048 UTF-8 bytes.

**Access scope:** The path permits itself and descendants at a segment boundary. `/view/abc` permits `/view/abc/thumbnail`, but not `/view/abcd`. The query does not restrict access. A scoped link works only through the resource host; access through a custom domain is denied. The Connector must also authorize the path before using it to look up an object.

**Path rules:** Start with one `/`. Use canonical, unescaped ASCII. A single trailing slash is allowed. Do not use `//`, percent escapes, semicolons, empty interior segments, `.` or `..` segments, exclamation marks, parentheses, asterisks, apostrophes, a scheme, host, backslash, fragment, whitespace, or control characters. Use opaque route keys for objects; keep user filenames in metadata. Clients must preserve the accepted path bytes. Percent-encoded alternatives are denied.

**Query rules:** Use ASCII letters, digits, `._~!$&()*+,;=:@/?-`, and well-formed percent escapes. Percent-encode other characters. Literal apostrophes are not accepted. Query bytes are preserved, including semicolons and percent escapes.

**Errors:** JSON null and non-string values return `400 validation_error`. Empty strings, oversized or malformed paths, and use on other resource types return `400 invalid_target_path`.

**Example:**

### CreateQurlForResourceRequest

- **Type:**`object`

Request body for `POST /v1/resources/{id}/qurls`. The target resource is identified by the URL path.

All fields are optional. An empty JSON object mints a qURL with the owner's plan defaults. An unrecognized field is rejected with 400 rather than ignored — see `CreateQurlRequest`.

- **`access_policy`**

  `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

  - **`ai_agent_policy`**

    `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

    - **`allow_categories`**

      `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`block_all`**

      `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

    - **`deny_categories`**

      `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

  - **`geo_allowlist`**

    `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`geo_denylist`**

    `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`ip_allowlist`**

    `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`ip_denylist`**

    `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`user_agent_allow_regex`**

    `string` — Regular expression to allow matching user agents (max 256 characters)

  - **`user_agent_deny_regex`**

    `string` — Regular expression to deny matching user agents (max 256 characters)

- **`expires_in`**

  `string` — How long the new link remains valid. Examples: \`5m\`, \`1h\`, \`7d\`, \`1w\`. Minimum: 1 minute. Maximum: 30 days on all plans. The resource's expiration can shorten the link's lifetime; read \`expires\_at\` in the response. When omitted or empty, qURL Connector links use 5 minutes. URL and integration-owned upload links use the service default. Set this field explicitly for a predictable lifetime.

- **`label`**

  `string` — Human-readable label identifying who this qURL is for.

- **`max_sessions`**

  `integer` — Maximum concurrent sessions for this qURL, not the whole resource. Use \`0\` for no limit or \`1\`–\`1000\` for a limit. Set \`one\_time\_use: false\` explicitly when you need multiple visitors: some resource types otherwise create a single-visitor link even when \`max\_sessions\` is \`0\`. Visitors that share a source IP count as one session and share its IP policy checks.

- **`one_time_use`**

  `boolean` — Whether the link is consumed by its first successful use. Explicit \`true\` or \`false\` takes precedence over the resource defaults. When omitted, URL and integration-owned upload links use \`false\`. qURL Connector links use \`true\` unless \`max\_sessions\` is greater than 1. For a reusable Connector link, explicitly send \`one\_time\_use: false\`, including when \`max\_sessions\` is \`0\` (unlimited).

- **`session_duration`**

  `string` — How long a session permits new requests after the recipient first reaches the content. Controls the session/cookie lifetime, separate from the qURL link expiration (expires\_in). Supports: seconds (30s), minutes (5m), hours (1h), days (1d). Minimum: 1 second. Maximum: 24 hours. Default: inherit the resource cap when set. Otherwise qURL Connectors use 1 hour and other resource types use the service default. New requests are denied at session expiry. Active requests, streams, and WebSockets are canceled at that deadline. See \`CreateQurlRequest.session\_duration\` → \*\*Timing precision\*\* for the full semantics.

- **`target_path`**

  `string` — Destination path and optional query for a qURL Connector link. For example, \`/view/abc?download=1\` redirects to that path on the resource's \`qurl.site\` host. Omit this field to use the host's root. Only qURL Connector resources (\`type=tunnel\`) accept it. Maximum: 2048 UTF-8 bytes. \*\*Access scope:\*\* The path permits itself and descendants at a segment boundary. \`/view/abc\` permits \`/view/abc/thumbnail\`, but not \`/view/abcd\`. The query does not restrict access. A scoped link works only through the resource host; access through a custom domain is denied. The Connector must also authorize the path before using it to look up an object. \*\*Path rules:\*\* Start with one \`/\`. Use canonical, unescaped ASCII. A single trailing slash is allowed. Do not use \`//\`, percent escapes, semicolons, empty interior segments, \`.\` or \`..\` segments, exclamation marks, parentheses, asterisks, apostrophes, a scheme, host, backslash, fragment, whitespace, or control characters. Use opaque route keys for objects; keep user filenames in metadata. Clients must preserve the accepted path bytes. Percent-encoded alternatives are denied. \*\*Query rules:\*\* Use ASCII letters, digits, \`.\_\~!$&()\*+,;=:@/?-\`, and well-formed percent escapes. Percent-encode other characters. Literal apostrophes are not accepted. Query bytes are preserved, including semicolons and percent escapes. \*\*Errors:\*\* JSON null and non-string values return \`400 validation\_error\`. Empty strings, oversized or malformed paths, and use on other resource types return \`400 invalid\_target\_path\`.

**Example:**

```json
{
  "expires_in": "1h",
  "one_time_use": true,
  "max_sessions": 0,
  "session_duration": "1h",
  "access_policy": {
    "ip_allowlist": [
      "192.168.1.0/24",
      "10.0.0.1"
    ],
    "ip_denylist": [
      ""
    ],
    "geo_allowlist": [
      "US",
      "CA",
      "GB"
    ],
    "geo_denylist": [
      "CN",
      "RU"
    ],
    "user_agent_allow_regex": "^Mozilla.*",
    "user_agent_deny_regex": ".*bot.*",
    "ai_agent_policy": {
      "block_all": false,
      "deny_categories": [
        "gptbot",
        "commoncrawl",
        "bytedance"
      ],
      "allow_categories": [
        "claude",
        "chatgpt",
        "perplexity"
      ]
    }
  },
  "label": "Alice from Acme",
  "target_path": "/view/9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}
```

### ResourceType

- **Type:**`string`

The kind of protected resource:

| Value     | Resource                                                    |
| --------- | ----------------------------------------------------------- |
| `url`     | A target URL. Default.                                      |
| `tunnel`  | A qURL Connector, available to permitted Connector clients. |
| `transit` | Content uploaded through an integration.                    |

See `ResourceData.type` for fields omitted from integration-owned upload responses.

**Example:**

### KnockResourceID

- **Type:**`string`

Internal use.

**Example:**

### ConnectorRoutingID

- **Type:**`string`

Opaque routing label the qURL Connector must use verbatim; do not derive it client-side.

**Example:**

### CRID

- **Type:**`string`

The preferred identifier for a resource. Use the `crid` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their `resource_id` public key as a fallback.

**Example:**

### ConsumerAccessPolicy

- **Type:**`object`

* **`mode` (required)**

  `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

* **`version` (required)**

  `integer`, format: `int64`

* **`contract_id`**

  `string` — Required only for contract mode. Names the resource-specific contract.

**Example:**

```json
{
  "mode": "keys",
  "version": 1,
  "contract_id": ""
}
```

### ConsumerVersionRequest

- **Type:**`object`

* **`expected_version` (required)**

  `integer`, format: `int64`

**Example:**

```json
{
  "expected_version": 1
}
```

### ConsumerInvitationRequest

- **Type:**`object`

* **`email` (required)**

  `string`, format: `email`

**Example:**

```json
{
  "email": ""
}
```

### ConsumerGrantApprovalRequest

- **Type:**`object`

Review approval uses only expected\_version. Key opt-in uses the literal grant\_id new and a credential. Use expected\_version zero for a first grant, or the current version for an explicit replacement. Contract fulfillment requires a verified entitlement and expiry from the publisher backend.

- **`expected_version` (required)**

  `integer`, format: `int64`

- **`credential_id`**

  `string`

- **`credential_kind`**

  `string`, possible values: `"passkey", "device"`

- **`credential_public_key_b64`**

  `string`

- **`entitlement_id`**

  `string`

- **`expires_at`**

  `string`, format: `date-time`

**Example:**

```json
{
  "expected_version": 0,
  "credential_kind": "passkey",
  "credential_id": "",
  "credential_public_key_b64": "",
  "entitlement_id": "",
  "expires_at": ""
}
```

### ConsumerSessionCleanup

- **Type:**`string`

Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

**Example:**

### ConsumerInvitation

- **Type:**`object`

* **`delivery` (required)**

  `string`, possible values: `"queued", "accepted", "failed"`

* **`email` (required)**

  `string`

* **`expires_at` (required)**

  `string`, format: `date-time`

* **`id` (required)**

  `string`

* **`status` (required)**

  `string`, possible values: `"open", "enrolled", "revoked"`

* **`version` (required)**

  `integer`, format: `int64`

* **`session_cleanup`**

  `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

**Example:**

```json
{
  "session_cleanup": "pending",
  "id": "",
  "email": "",
  "status": "open",
  "delivery": "queued",
  "version": 1,
  "expires_at": ""
}
```

### ConsumerGrant

- **Type:**`object`

Contract mode omits credential and contact identity. Email is returned only after mailbox proof and disclosure consent.

- **`id` (required)**

  `string`

- **`status` (required)**

  `string`, possible values: `"pending", "active", "revoked"`

- **`version` (required)**

  `integer`, format: `int64`

- **`credential_id`**

  `string`

- **`credential_kind`**

  `string`, possible values: `"passkey", "device", "email"`

- **`credential_public_key_b64`**

  `string`

- **`email`**

  `string`

- **`email_verified_at`**

  `string`, format: `date-time`

- **`entitlement_id`**

  `string`

- **`expires_at`**

  `string`, format: `date-time`

- **`session_cleanup`**

  `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

**Example:**

```json
{
  "session_cleanup": "pending",
  "id": "",
  "status": "pending",
  "version": 1,
  "credential_kind": "passkey",
  "credential_id": "",
  "credential_public_key_b64": "",
  "email": "",
  "email_verified_at": "",
  "expires_at": "",
  "entitlement_id": ""
}
```

### ConsumerInvitationResponse

- **Type:**`object`

* **`data` (required)**

  `object`

  - **`delivery` (required)**

    `string`, possible values: `"queued", "accepted", "failed"`

  - **`email` (required)**

    `string`

  - **`expires_at` (required)**

    `string`, format: `date-time`

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"open", "enrolled", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "session_cleanup": "pending",
    "id": "",
    "email": "",
    "status": "open",
    "delivery": "queued",
    "version": 1,
    "expires_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### ConsumerInvitationListResponse

- **Type:**`object`

* **`data` (required)**

  `array`

  **Items:**

  - **`delivery` (required)**

    `string`, possible values: `"queued", "accepted", "failed"`

  - **`email` (required)**

    `string`

  - **`expires_at` (required)**

    `string`, format: `date-time`

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"open", "enrolled", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "session_cleanup": "pending",
      "id": "",
      "email": "",
      "status": "open",
      "delivery": "queued",
      "version": 1,
      "expires_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### ConsumerGrantResponse

- **Type:**`object`

* **`data` (required)**

  `object` — Contract mode omits credential and contact identity. Email is returned only after mailbox proof and disclosure consent.

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"pending", "active", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`credential_id`**

    `string`

  - **`credential_kind`**

    `string`, possible values: `"passkey", "device", "email"`

  - **`credential_public_key_b64`**

    `string`

  - **`email`**

    `string`

  - **`email_verified_at`**

    `string`, format: `date-time`

  - **`entitlement_id`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "session_cleanup": "pending",
    "id": "",
    "status": "pending",
    "version": 1,
    "credential_kind": "passkey",
    "credential_id": "",
    "credential_public_key_b64": "",
    "email": "",
    "email_verified_at": "",
    "expires_at": "",
    "entitlement_id": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### ConsumerGrantListResponse

- **Type:**`object`

* **`data` (required)**

  `array`

  **Items:**

  - **`id` (required)**

    `string`

  - **`status` (required)**

    `string`, possible values: `"pending", "active", "revoked"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`credential_id`**

    `string`

  - **`credential_kind`**

    `string`, possible values: `"passkey", "device", "email"`

  - **`credential_public_key_b64`**

    `string`

  - **`email`**

    `string`

  - **`email_verified_at`**

    `string`, format: `date-time`

  - **`entitlement_id`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`session_cleanup`**

    `string`, possible values: `"pending", "complete"` — Complete is reported only when no active sessions can remain. Revoked access can still require stream cleanup.

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "session_cleanup": "pending",
      "id": "",
      "status": "pending",
      "version": 1,
      "credential_kind": "passkey",
      "credential_id": "",
      "credential_public_key_b64": "",
      "email": "",
      "email_verified_at": "",
      "expires_at": "",
      "entitlement_id": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### CreateResourceRequest

- **Type:**`object`

Registers a resource. URL resources are matched by your account and `target_url`. If one already exists with the same type, the response returns it with `201`.

| Existing URL resource | Requested alias                      | Result                                                        |
| --------------------- | ------------------------------------ | ------------------------------------------------------------- |
| None                  | Omitted or available                 | Create the resource.                                          |
| Present               | Omitted or unchanged                 | Return it unchanged.                                          |
| Present               | Different, including adding an alias | `409 resource_alias_mismatch`; use PATCH to change the alias. |

An alias used by another of your resources returns `409 alias_in_use`. The same URL registered with a different type returns `409 resource_type_conflict`. For example, `url` cannot reuse a `transit` resource, or vice versa. Use the existing resource with its current type, or revoke it and create a new one. Revoking the resource also blocks its existing links.

qURL Connectors (`type=tunnel`) are matched by `slug` when `find_or_create` is true. They do not reuse resources by target URL. See those fields for retry behavior.

- **`access_requests`**

  `boolean` — When true, someone who opens this private resource's link without access can ask you for it. You approve or deny each request. When omitted, a new resource accepts no access requests. Only a private resource can accept access requests: \`true\` together with a public resource returns \`400 invalid\_input\`. When the request returns an existing resource, this field changes nothing: the response carries the value the resource has. Change it with \`PATCH /v1/resources/{id}\`.

- **`alias`**

  `string` — Optional human-readable handle for the resource, unique per owner. Format: 3–64 lowercase alphanumeric chars and hyphens, must start with a letter and end alphanumeric. Reserved words (e.g. \`admin\`, \`qurl\`, \`tunnel\`) are rejected with 400 \`alias\_reserved\`. Collisions with another of your resources return 409 \`alias\_in\_use\`. Disagreeing with the alias of an existing resource for the same target URL returns 409 \`resource\_alias\_mismatch\`.

- **`allowed_device_keys`**

  `array` — Public X25519 keys of the devices that can create links for this private resource, in canonical padded standard base64. Only the publisher can change this list; with an empty list only the owner can create links. The list has no effect unless the resource is private. When the request returns an existing resource, omit the list or send the keys that resource lists, in any order. Other keys return \`400 device\_keys\_mismatch\` and change nothing, because a create request does not change the allowed devices of a resource that exists. Change the list with \`PATCH /v1/resources/{id}\` (\`allowed\_device\_keys\_add\`, \`allowed\_device\_keys\_remove\`).

  **Items:**

  `string`

- **`consumer_access`**

  `object`

  - **`mode` (required)**

    `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`contract_id`**

    `string` — Required only for contract mode. Names the resource-specific contract.

- **`custom_domain`**

  `string` — Optional custom domain to bind to this resource. Must be a domain previously verified via the domains API.

- **`description`**

  `string`

- **`find_or_create`**

  `boolean`, default: `false` — Reuse an active qURL Connector with the same slug in your account. Default: \`false\`. Requires \`type=tunnel\` and \`slug\`; otherwise returns \`400\`. | Existing Connector | Result when true | |---|---| | Active | Return it with \`201\` and \`meta.found\_existing: true\`. | | None | Create one with \`201\` and \`meta.found\_existing: false\`. | | Revoked | Create a new resource with \`201\` and \`meta.found\_existing: false\`. The old CRID and links remain revoked. | | Closed | Return \`410\`; choose a new slug or delete the retained resource before retrying. | Reusing a Connector ignores \`alias\`, \`description\`, and \`tags\`; use PATCH to change them. Concurrent requests resolve to one resource. On \`409 slug\_in\_use\`, retry once, then inspect the existing Connector before deciding to revoke it. Success consumes a restricted Connector enrollment credential, including reuse. For recovery after \`500\`, \`401\`, or \`409 bootstrap\_key\_consumed\`, see \`CreateApiKeyRequest.kind\`.

- **`private`**

  `boolean` — When true, only the owner and the devices listed in \`allowed\_device\_keys\` can create links for this resource. A resource that is not private is public. When omitted, a new URL or qURL Connector resource is private. Send \`false\` to create a public one. Privacy cannot change after creation. When the request returns an existing resource, omit this field: the resource keeps the privacy it has. A value that differs from the existing resource returns \`400 privacy\_mismatch\`.

- **`slug`**

  `string` — Stable name for a qURL Connector within your account. Required for \`type=tunnel\`; forbidden for other resource types. Use 3–64 lowercase letters, digits, or hyphens, starting with a letter and ending with a letter or digit. There are no reserved names. A slug cannot change after creation. An \`alias\` can change and is intended for display. Use \`GET /v1/resources?slug={slug}\` to find the Connector after a process restart. Without \`find\_or\_create: true\`, an existing slug returns \`409 slug\_in\_use\`. See \`find\_or\_create\` for reuse and retry behavior. A missing slug returns \`400\` for account credentials, or \`403\` for a restricted Connector enrollment credential whose permission requires a bound slug. Use \`error.code\` to handle errors.

- **`tags`**

  `array`

  **Items:**

  `string`

- **`target_url`**

  `string`, format: `uri` — The URL to protect.

- **`type`**

  `string`, possible values: `"url", "tunnel", "transit"`, default: `"url"` — The kind of protected resource: | Value | Resource | |---|---| | \`url\` | A target URL. Default. | | \`tunnel\` | A qURL Connector, available to permitted Connector clients. | | \`transit\` | Content uploaded through an integration. | See \`ResourceData.type\` for fields omitted from integration-owned upload responses.

**Example:**

```json
{
  "consumer_access": {
    "mode": "keys",
    "version": 1,
    "contract_id": ""
  },
  "private": true,
  "access_requests": true,
  "allowed_device_keys": [
    ""
  ],
  "type": "url",
  "target_url": "",
  "description": "",
  "tags": [
    ""
  ],
  "custom_domain": "",
  "alias": "dev-dashboard",
  "slug": "prod-dashboard",
  "find_or_create": true
}
```

### UpdateResourceRequest

- **Type:**`object`

* **`access_requests`**

  `boolean` — Turn access requests on or off. When on, someone who opens this private resource's link without access can ask you for it, and you approve or deny each request. Only a private resource can turn it on: \`true\` for a public resource returns \`400 invalid\_input\`. Turning it off stops new requests, and people without access are no longer told that the resource exists. It removes nothing: requests that are already pending stay in the list until you decide them or they expire, you can still approve them, and passkeys you approved keep their access. Update separately from \`custom\_domain\`; combining them returns \`400 invalid\_input\`.

* **`alias`**

  `string | null` — Set, rebind, or clear the resource's alias under RFC 7396 JSON Merge Patch semantics: - omitted → no change. - \`null\` → clear the alias and make the alias available again. Equivalent to deleting the alias. - string value → set or rebind. Pattern, length, and reserved-word rules match \`CreateResourceRequest.alias\`. Sending an empty string (\`""\`) is rejected with \`400 alias\_invalid\_format\` to avoid the silent-no-op trap on callers building this body from user input — use \`null\` for the clear directive.

* **`allowed_device_keys`**

  `array` — Replace the complete list of public X25519 keys of the devices that can create links for this private resource. Use canonical padded standard base64. Only the publisher can change this list; with an empty list only the owner can create links. The list has no effect unless the resource is private. Update separately from custom\_domain; combining them returns 400 invalid\_input. Cannot be combined with \`allowed\_device\_keys\_add\` or \`allowed\_device\_keys\_remove\`. To change single keys without sending the whole list, use those two fields instead.

  **Items:**

  `string`

* **`allowed_device_keys_add`**

  `array` — Public X25519 keys to add to the devices that can create links for this private resource, in canonical padded standard base64. The other listed keys stay as they are. A key that is already listed changes nothing. The resulting list can hold at most 256 keys, counted after the removals of the same request; a request that would exceed that returns \`400 invalid\_input\` and changes nothing. Can be sent together with \`allowed\_device\_keys\_remove\`; a key in both returns \`400 invalid\_input\`. Cannot be combined with \`allowed\_device\_keys\` or \`custom\_domain\` (\`400 invalid\_input\`).

  **Items:**

  `string`

* **`allowed_device_keys_remove`**

  `array` — Public X25519 keys to remove from the devices that can create links for this private resource, in canonical padded standard base64. The other listed keys stay as they are. A key that is not listed changes nothing. Removing a key blocks new link requests from that device; links it already created keep working until they expire or are revoked. Can be sent together with \`allowed\_device\_keys\_add\`; a key in both returns \`400 invalid\_input\`. Cannot be combined with \`allowed\_device\_keys\` or \`custom\_domain\` (\`400 invalid\_input\`).

  **Items:**

  `string`

* **`consumer_access`**

  `object`

  - **`mode` (required)**

    `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`contract_id`**

    `string` — Required only for contract mode. Names the resource-specific contract.

* **`custom_domain`**

  `string`

* **`description`**

  `string`

* **`expected_consumer_access_version`**

  `integer`, format: `int64` — Required when consumer\_access is changed. Zero means no prior consumer policy.

* **`preserve_host`**

  `boolean` — Whether to preserve the original Host header when proxying to the origin via a custom domain. When true, the custom domain is sent as the Host header. When false (default), the target server's hostname is used. Only meaningful when custom\_domain is set.

* **`tags`**

  `array`

  **Items:**

  `string`

**Example:**

```json
{
  "consumer_access": {
    "mode": "keys",
    "version": 1,
    "contract_id": ""
  },
  "expected_consumer_access_version": 0,
  "access_requests": true,
  "allowed_device_keys": [
    ""
  ],
  "allowed_device_keys_add": [
    ""
  ],
  "allowed_device_keys_remove": [
    ""
  ],
  "description": "",
  "tags": [
    ""
  ],
  "custom_domain": "",
  "preserve_host": true,
  "alias": null
}
```

### ResourceData

- **Type:**`object`

* **`access_requests`**

  `boolean` — Whether someone who opens this private resource's link without access can ask you for it. Always present. Change it with \`PATCH /v1/resources/{id}\`.

* **`alias`**

  `string | null` — Per-owner human-readable handle for the resource (e.g. \`dev-dashboard\`). \`null\` when no alias is set. Lookup via \`GET /v1/resources?alias={alias}\` (returns a 0- or 1-item list).

* **`allowed_device_keys`**

  `array` — Public X25519 keys of the devices that can create links for this private resource, in canonical padded standard base64. Omitted when no devices are listed; only the owner can then create links for a private resource. The list has no effect unless the resource is private.

  **Items:**

  `string`

* **`allowed_passkeys`**

  `array` — The passkeys you approved for this private resource, oldest approval first. Always present; empty when you approved none. A passkey is added by approving an access request and removed with \`DELETE /v1/resources/{id}/allowed-passkeys/{device\_id}\`.

  **Items:**

  - **`approved_at` (required)**

    `string`, format: `date-time` — When you approved the request.

  - **`device_id` (required)**

    `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

  - **`name` (required)**

    `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

* **`connector_routing_id`**

  `string` — Opaque routing label the qURL Connector must use verbatim; do not derive it client-side.

* **`consumer_access`**

  `object`

  - **`mode` (required)**

    `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

  - **`version` (required)**

    `integer`, format: `int64`

  - **`contract_id`**

    `string` — Required only for contract mode. Names the resource-specific contract.

* **`consumer_access_version`**

  `integer`, format: `int64` — Monotonic consumer policy revision. Zero means no consumer policy has been set.

* **`created_at`**

  `string`, format: `date-time`

* **`crid`**

  `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

* **`custom_domain`**

  `string | null`

* **`description`**

  `string`

* **`desired_state`**

  `string`, possible values: `"on", "off"`

* **`expires_at`**

  `string`, format: `date-time`

* **`knock_resource_id`**

  `string` — Internal use.

* **`preserve_host`**

  `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

* **`private`**

  `boolean`, default: `false` — When true, only the owner and the devices listed in \`allowed\_device\_keys\` can create links for this resource. A resource that is not private is public. Privacy is chosen when the resource is created (a new resource is private unless its creator asked for a public one) and cannot change afterward. Older resources can omit this field; they are public.

* **`publisher`**

  `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

  - **`verified` (required)**

    `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

  - **`name`**

    `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

* **`qurl_count`**

  `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

* **`resource_id`**

  `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

* **`serving_epoch`**

  `integer`, format: `int64` — Monotonic qURL Connector serving lifecycle. Zero means sharing has never been started.

* **`session_duration_cap`**

  `integer` — Maximum session duration, in seconds, for qURLs on this resource. It is fixed when \`POST /v1/qurls\` first creates the resource. Later link creation and updates cannot exceed it (\`400 invalid\_session\_duration\`). Omitted session durations inherit the cap. Absent means there is no resource cap; the 1-second to 24-hour field limits still apply. \`POST /v1/resources\` creates a resource without a cap, and later link creation does not add one. To change a cap, register a different target URL, or revoke this resource and recreate it through \`POST /v1/qurls\` with the required \`session\_duration\`. Revocation blocks all existing links. A new resource receives a new CRID.

* **`slug`**

  `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. Distinct from \`alias\`: slugs are immutable post-create, aliases are mutable. Lookup via \`GET /v1/resources?slug={slug}\` returns a 0- or 1-item active-resource list. See \`CreateResourceRequest.slug\` for format, uniqueness, and \`find\_or\_create\` semantics.

* **`status`**

  `string`, possible values: `"active", "revoked"`

* **`tags`**

  `array`

  **Items:**

  `string`

* **`target_url`**

  `string` — The URL to protect. Optional — omitted (field absent from the JSON object, NOT serialized as null) when not applicable to this resource, or when the resource is owned by a connector.

* **`tombstoned_at`**

  `string`, format: `date-time` — When the resource's lifecycle closed. Present only on tombstoned resources; absent on active ones. Consumers fetching a single resource by ID handle the tombstoned-row case via the 410 response on \`GET /v1/resources/{id}\` — the same timestamp surfaces in \`meta.tombstone.tombstoned\_at\`. List / alias-lookup (\`GET /v1/resources?alias=...\`) responses include this field on the resource itself so callers can distinguish tombstoned from active without a per-row probe.

* **`type`**

  `string` — Resource type (see \`ResourceType\` for the full enum). Management-API read responses on connector-owned upload rows redact user-content resource fields (\`target\_url\`, \`description\`, \`tags\`, \`custom\_domain\`, \`alias\`, \`preserve\_host\`, \`qurl\_count\`, \`slug\`) and nested qURL summary fields (\`label\`, \`target\_path\`). List/detail responses include the row but omit those fields.

**Example:**

```json
{
  "consumer_access_version": 1,
  "consumer_access": {
    "mode": "keys",
    "version": 1,
    "contract_id": ""
  },
  "private": false,
  "allowed_device_keys": [
    ""
  ],
  "access_requests": true,
  "allowed_passkeys": [
    {
      "device_id": "k3wp-x2aq-7mzn-4tby",
      "name": "",
      "approved_at": ""
    }
  ],
  "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
  "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
  "type": "",
  "target_url": "",
  "knock_resource_id": "",
  "connector_routing_id": "",
  "status": "active",
  "desired_state": "on",
  "serving_epoch": 0,
  "description": "",
  "tags": [
    ""
  ],
  "custom_domain": null,
  "alias": null,
  "slug": "",
  "preserve_host": false,
  "session_duration_cap": 30,
  "qurl_count": 1,
  "created_at": "",
  "expires_at": "",
  "publisher": {
    "name": "Acme Docs",
    "verified": false
  },
  "tombstoned_at": ""
}
```

### ResourceDetailData

- **Type:**`object`

* **`qurls`**

  `array` — Unordered bounded preview of per-qURL summaries for this resource (maximum 100). This preview does not imply newest first or any stable ordering and is not active-first; an active qURL can be absent from the preview when earlier qURLs fill the cap. When present, \`resource.qurl\_count\` is the exact total; if omitted, callers must treat truncation as unknown. For the complete per-resource qURL list, use \`GET /v1/resources/{id}/qurls\`, which pages through every qURL with cursor-based pagination. For large resources, the preview and count are separate reads, so concurrent qURL changes can make the preview length and \`resource.qurl\_count\` briefly differ.

  **Items:**

  - **`access_policy`**

    `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

    - **`ai_agent_policy`**

      `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

      - **`allow_categories`**

        `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`block_all`**

        `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

      - **`deny_categories`**

        `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`geo_allowlist`**

      `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`geo_denylist`**

      `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`ip_allowlist`**

      `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`ip_denylist`**

      `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`user_agent_allow_regex`**

      `string` — Regular expression to allow matching user agents (max 256 characters)

    - **`user_agent_deny_regex`**

      `string` — Regular expression to deny matching user agents (max 256 characters)

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`label`**

    `string`

  - **`max_sessions`**

    `integer`

  - **`one_time_use`**

    `boolean`

  - **`qurl_id`**

    `string`

  - **`qurl_site`**

    `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

  - **`session_duration`**

    `integer` — Session lifetime in seconds (0 = server default)

  - **`status`**

    `string`, possible values: `"active", "consumed", "expired", "revoked"`

  - **`target_path`**

    `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

  - **`use_count`**

    `integer`

* **`resource`**

  `object`

  - **`access_requests`**

    `boolean` — Whether someone who opens this private resource's link without access can ask you for it. Always present. Change it with \`PATCH /v1/resources/{id}\`.

  - **`alias`**

    `string | null` — Per-owner human-readable handle for the resource (e.g. \`dev-dashboard\`). \`null\` when no alias is set. Lookup via \`GET /v1/resources?alias={alias}\` (returns a 0- or 1-item list).

  - **`allowed_device_keys`**

    `array` — Public X25519 keys of the devices that can create links for this private resource, in canonical padded standard base64. Omitted when no devices are listed; only the owner can then create links for a private resource. The list has no effect unless the resource is private.

    **Items:**

    `string`

  - **`allowed_passkeys`**

    `array` — The passkeys you approved for this private resource, oldest approval first. Always present; empty when you approved none. A passkey is added by approving an access request and removed with \`DELETE /v1/resources/{id}/allowed-passkeys/{device\_id}\`.

    **Items:**

    - **`approved_at` (required)**

      `string`, format: `date-time` — When you approved the request.

    - **`device_id` (required)**

      `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

    - **`name` (required)**

      `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

  - **`connector_routing_id`**

    `string` — Opaque routing label the qURL Connector must use verbatim; do not derive it client-side.

  - **`consumer_access`**

    `object`

    - **`mode` (required)**

      `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

    - **`version` (required)**

      `integer`, format: `int64`

    - **`contract_id`**

      `string` — Required only for contract mode. Names the resource-specific contract.

  - **`consumer_access_version`**

    `integer`, format: `int64` — Monotonic consumer policy revision. Zero means no consumer policy has been set.

  - **`created_at`**

    `string`, format: `date-time`

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`desired_state`**

    `string`, possible values: `"on", "off"`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`knock_resource_id`**

    `string` — Internal use.

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`private`**

    `boolean`, default: `false` — When true, only the owner and the devices listed in \`allowed\_device\_keys\` can create links for this resource. A resource that is not private is public. Privacy is chosen when the resource is created (a new resource is private unless its creator asked for a public one) and cannot change afterward. Older resources can omit this field; they are public.

  - **`publisher`**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`qurl_count`**

    `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

  - **`serving_epoch`**

    `integer`, format: `int64` — Monotonic qURL Connector serving lifecycle. Zero means sharing has never been started.

  - **`session_duration_cap`**

    `integer` — Maximum session duration, in seconds, for qURLs on this resource. It is fixed when \`POST /v1/qurls\` first creates the resource. Later link creation and updates cannot exceed it (\`400 invalid\_session\_duration\`). Omitted session durations inherit the cap. Absent means there is no resource cap; the 1-second to 24-hour field limits still apply. \`POST /v1/resources\` creates a resource without a cap, and later link creation does not add one. To change a cap, register a different target URL, or revoke this resource and recreate it through \`POST /v1/qurls\` with the required \`session\_duration\`. Revocation blocks all existing links. A new resource receives a new CRID.

  - **`slug`**

    `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. Distinct from \`alias\`: slugs are immutable post-create, aliases are mutable. Lookup via \`GET /v1/resources?slug={slug}\` returns a 0- or 1-item active-resource list. See \`CreateResourceRequest.slug\` for format, uniqueness, and \`find\_or\_create\` semantics.

  - **`status`**

    `string`, possible values: `"active", "revoked"`

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `string` — The URL to protect. Optional — omitted (field absent from the JSON object, NOT serialized as null) when not applicable to this resource, or when the resource is owned by a connector.

  - **`tombstoned_at`**

    `string`, format: `date-time` — When the resource's lifecycle closed. Present only on tombstoned resources; absent on active ones. Consumers fetching a single resource by ID handle the tombstoned-row case via the 410 response on \`GET /v1/resources/{id}\` — the same timestamp surfaces in \`meta.tombstone.tombstoned\_at\`. List / alias-lookup (\`GET /v1/resources?alias=...\`) responses include this field on the resource itself so callers can distinguish tombstoned from active without a per-row probe.

  - **`type`**

    `string` — Resource type (see \`ResourceType\` for the full enum). Management-API read responses on connector-owned upload rows redact user-content resource fields (\`target\_url\`, \`description\`, \`tags\`, \`custom\_domain\`, \`alias\`, \`preserve\_host\`, \`qurl\_count\`, \`slug\`) and nested qURL summary fields (\`label\`, \`target\_path\`). List/detail responses include the row but omit those fields.

**Example:**

```json
{
  "resource": {
    "consumer_access_version": 1,
    "consumer_access": {
      "mode": "keys",
      "version": 1,
      "contract_id": ""
    },
    "private": false,
    "allowed_device_keys": [
      ""
    ],
    "access_requests": true,
    "allowed_passkeys": [
      {
        "device_id": "k3wp-x2aq-7mzn-4tby",
        "name": "",
        "approved_at": ""
      }
    ],
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "type": "",
    "target_url": "",
    "knock_resource_id": "",
    "connector_routing_id": "",
    "status": "active",
    "desired_state": "on",
    "serving_epoch": 0,
    "description": "",
    "tags": [
      ""
    ],
    "custom_domain": null,
    "alias": null,
    "slug": "",
    "preserve_host": false,
    "session_duration_cap": 30,
    "qurl_count": 1,
    "created_at": "",
    "expires_at": "",
    "publisher": {
      "name": "Acme Docs",
      "verified": false
    },
    "tombstoned_at": ""
  },
  "qurls": [
    {
      "qurl_id": "",
      "label": "",
      "status": "active",
      "one_time_use": true,
      "max_sessions": 1,
      "session_duration": 1,
      "use_count": 1,
      "qurl_site": "",
      "target_path": "",
      "created_at": "",
      "expires_at": "",
      "access_policy": {
        "ip_allowlist": [
          "192.168.1.0/24",
          "10.0.0.1"
        ],
        "ip_denylist": [
          ""
        ],
        "geo_allowlist": [
          "US",
          "CA",
          "GB"
        ],
        "geo_denylist": [
          "CN",
          "RU"
        ],
        "user_agent_allow_regex": "^Mozilla.*",
        "user_agent_deny_regex": ".*bot.*",
        "ai_agent_policy": {
          "block_all": false,
          "deny_categories": [
            "gptbot",
            "commoncrawl",
            "bytedance"
          ],
          "allow_categories": [
            "claude",
            "chatgpt",
            "perplexity"
          ]
        }
      }
    }
  ]
}
```

### QurlSummary

- **Type:**`object`

* **`access_policy`**

  `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

  - **`ai_agent_policy`**

    `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

    - **`allow_categories`**

      `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`block_all`**

      `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

    - **`deny_categories`**

      `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

  - **`geo_allowlist`**

    `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`geo_denylist`**

    `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`ip_allowlist`**

    `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`ip_denylist`**

    `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`user_agent_allow_regex`**

    `string` — Regular expression to allow matching user agents (max 256 characters)

  - **`user_agent_deny_regex`**

    `string` — Regular expression to deny matching user agents (max 256 characters)

* **`created_at`**

  `string`, format: `date-time`

* **`expires_at`**

  `string`, format: `date-time`

* **`label`**

  `string`

* **`max_sessions`**

  `integer`

* **`one_time_use`**

  `boolean`

* **`qurl_id`**

  `string`

* **`qurl_site`**

  `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

* **`session_duration`**

  `integer` — Session lifetime in seconds (0 = server default)

* **`status`**

  `string`, possible values: `"active", "consumed", "expired", "revoked"`

* **`target_path`**

  `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

* **`use_count`**

  `integer`

**Example:**

```json
{
  "qurl_id": "",
  "label": "",
  "status": "active",
  "one_time_use": true,
  "max_sessions": 1,
  "session_duration": 1,
  "use_count": 1,
  "qurl_site": "",
  "target_path": "",
  "created_at": "",
  "expires_at": "",
  "access_policy": {
    "ip_allowlist": [
      "192.168.1.0/24",
      "10.0.0.1"
    ],
    "ip_denylist": [
      ""
    ],
    "geo_allowlist": [
      "US",
      "CA",
      "GB"
    ],
    "geo_denylist": [
      "CN",
      "RU"
    ],
    "user_agent_allow_regex": "^Mozilla.*",
    "user_agent_deny_regex": ".*bot.*",
    "ai_agent_policy": {
      "block_all": false,
      "deny_categories": [
        "gptbot",
        "commoncrawl",
        "bytedance"
      ],
      "allow_categories": [
        "claude",
        "chatgpt",
        "perplexity"
      ]
    }
  }
}
```

### UpdateQurlTokenRequest

- **Type:**`object`

Update a specific qURL token. Can change expiration, label, access policy, or max\_sessions. At least one field must be provided. For expiry: provide exactly one of extend\_by (relative) or expires\_at (absolute). Active signed qURL links can be extended without changing the URL. They accept only an unchanged or empty access policy; see `PATCH /v1/resources/{id}/qurls/{qurl_id}`.

- **`access_policy`**

  `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

  - **`ai_agent_policy`**

    `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

    - **`allow_categories`**

      `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`block_all`**

      `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

    - **`deny_categories`**

      `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

  - **`geo_allowlist`**

    `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`geo_denylist`**

    `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`ip_allowlist`**

    `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`ip_denylist`**

    `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`user_agent_allow_regex`**

    `string` — Regular expression to allow matching user agents (max 256 characters)

  - **`user_agent_deny_regex`**

    `string` — Regular expression to deny matching user agents (max 256 characters)

- **`expires_at`**

  `string`, format: `date-time` — Absolute expiration timestamp (RFC 3339). Must be in the future and within 30 days. Mutually exclusive with extend\_by.

- **`extend_by`**

  `string` — Duration to extend by (e.g., "24h", "7d"). Minimum: 1 minute. Maximum: 30 days. Mutually exclusive with expires\_at.

- **`label`**

  `string` — Human-readable label for this qURL.

- **`max_sessions`**

  `integer` — Session limit for this qURL token (0 = unlimited). On signed qURL links it counts every session the link has admitted over its lifetime, not concurrent sessions: ending or terminating a session does not free a slot, and lowering the limit to the number of times the link has already been opened or fewer permanently prevents new sessions. Sessions that are already open are not affected.

- **`session_duration`**

  `string` — How long a session permits new requests after the recipient first reaches the content. Minimum: 1 second. Maximum: 24 hours. If the parent resource was created with a \`session\_duration\` (see \`ResourceData.session\_duration\_cap\`), values exceeding that cap are rejected with HTTP 400 \`invalid\_session\_duration\`. On a capped resource, sending an empty string for this field applies the cap rather than the server default — the uploader's intent supersedes the global default whenever a cap is in force. On an uncapped resource, an empty string resets to the default a new link would get: 1 hour for tunnel resources, and for URL resources the 24-hour-plus-10-second deadline described under \`CreateQurlRequest.session\_duration\`. New requests are denied at session expiry. Active requests, streams, and WebSockets are canceled at that deadline. See \`CreateQurlRequest.session\_duration\` → \*\*Timing precision\*\* for the full semantics.

**Example:**

```json
{
  "extend_by": "1h",
  "expires_at": "",
  "label": "",
  "access_policy": {
    "ip_allowlist": [
      "192.168.1.0/24",
      "10.0.0.1"
    ],
    "ip_denylist": [
      ""
    ],
    "geo_allowlist": [
      "US",
      "CA",
      "GB"
    ],
    "geo_denylist": [
      "CN",
      "RU"
    ],
    "user_agent_allow_regex": "^Mozilla.*",
    "user_agent_deny_regex": ".*bot.*",
    "ai_agent_policy": {
      "block_all": false,
      "deny_categories": [
        "gptbot",
        "commoncrawl",
        "bytedance"
      ],
      "allow_categories": [
        "claude",
        "chatgpt",
        "perplexity"
      ]
    }
  },
  "max_sessions": 0,
  "session_duration": "1h"
}
```

### QurlSummaryResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`access_policy`**

    `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

    - **`ai_agent_policy`**

      `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

      - **`allow_categories`**

        `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`block_all`**

        `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

      - **`deny_categories`**

        `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`geo_allowlist`**

      `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`geo_denylist`**

      `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`ip_allowlist`**

      `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`ip_denylist`**

      `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`user_agent_allow_regex`**

      `string` — Regular expression to allow matching user agents (max 256 characters)

    - **`user_agent_deny_regex`**

      `string` — Regular expression to deny matching user agents (max 256 characters)

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`label`**

    `string`

  - **`max_sessions`**

    `integer`

  - **`one_time_use`**

    `boolean`

  - **`qurl_id`**

    `string`

  - **`qurl_site`**

    `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

  - **`session_duration`**

    `integer` — Session lifetime in seconds (0 = server default)

  - **`status`**

    `string`, possible values: `"active", "consumed", "expired", "revoked"`

  - **`target_path`**

    `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

  - **`use_count`**

    `integer`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "qurl_id": "",
    "label": "",
    "status": "active",
    "one_time_use": true,
    "max_sessions": 1,
    "session_duration": 1,
    "use_count": 1,
    "qurl_site": "",
    "target_path": "",
    "created_at": "",
    "expires_at": "",
    "access_policy": {
      "ip_allowlist": [
        "192.168.1.0/24",
        "10.0.0.1"
      ],
      "ip_denylist": [
        ""
      ],
      "geo_allowlist": [
        "US",
        "CA",
        "GB"
      ],
      "geo_denylist": [
        "CN",
        "RU"
      ],
      "user_agent_allow_regex": "^Mozilla.*",
      "user_agent_deny_regex": ".*bot.*",
      "ai_agent_policy": {
        "block_all": false,
        "deny_categories": [
          "gptbot",
          "commoncrawl",
          "bytedance"
        ],
        "allow_categories": [
          "claude",
          "chatgpt",
          "perplexity"
        ]
      }
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### QurlSummaryListResponse

- **Type:**`object`

A cursor-paginated page of per-qURL summaries for a single resource, returned by `GET /v1/resources/{id}/qurls`.

- **`data`**

  `array`

  **Items:**

  - **`access_policy`**

    `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

    - **`ai_agent_policy`**

      `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

      - **`allow_categories`**

        `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`block_all`**

        `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

      - **`deny_categories`**

        `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`geo_allowlist`**

      `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`geo_denylist`**

      `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`ip_allowlist`**

      `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`ip_denylist`**

      `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`user_agent_allow_regex`**

      `string` — Regular expression to allow matching user agents (max 256 characters)

    - **`user_agent_deny_regex`**

      `string` — Regular expression to deny matching user agents (max 256 characters)

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`label`**

    `string`

  - **`max_sessions`**

    `integer`

  - **`one_time_use`**

    `boolean`

  - **`qurl_id`**

    `string`

  - **`qurl_site`**

    `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

  - **`session_duration`**

    `integer` — Session lifetime in seconds (0 = server default)

  - **`status`**

    `string`, possible values: `"active", "consumed", "expired", "revoked"`

  - **`target_path`**

    `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

  - **`use_count`**

    `integer`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "qurl_id": "",
      "label": "",
      "status": "active",
      "one_time_use": true,
      "max_sessions": 1,
      "session_duration": 1,
      "use_count": 1,
      "qurl_site": "",
      "target_path": "",
      "created_at": "",
      "expires_at": "",
      "access_policy": {
        "ip_allowlist": [
          "192.168.1.0/24",
          "10.0.0.1"
        ],
        "ip_denylist": [
          ""
        ],
        "geo_allowlist": [
          "US",
          "CA",
          "GB"
        ],
        "geo_denylist": [
          "CN",
          "RU"
        ],
        "user_agent_allow_regex": "^Mozilla.*",
        "user_agent_deny_regex": ".*bot.*",
        "ai_agent_policy": {
          "block_all": false,
          "deny_categories": [
            "gptbot",
            "commoncrawl",
            "bytedance"
          ],
          "allow_categories": [
            "claude",
            "chatgpt",
            "perplexity"
          ]
        }
      }
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### ResourceSharingDesiredState

- **Type:**`string`

**Example:**

### ResourceConnectionState

- **Type:**`string`

GET derives this from authenticated Connector connections for the current lifecycle. A lifecycle mutation that rotates `serving_epoch` reports `connecting` conservatively until a later GET observes a ready connection.

**Example:**

### UpdateResourceSharingRequest

- **Type:**`object`

* **`desired_state` (required)**

  `string`, possible values: `"on", "off"`

**Example:**

```json
{
  "desired_state": "on"
}
```

### ResourceSharingData

- **Type:**`object`

* **`connection_state` (required)**

  `string`, possible values: `"stopped", "connecting", "serving"` — GET derives this from authenticated Connector connections for the current lifecycle. A lifecycle mutation that rotates \`serving\_epoch\` reports \`connecting\` conservatively until a later GET observes a ready connection.

* **`created_at` (required)**

  `string`, format: `date-time` — When this Connector resource was created.

* **`crid` (required)**

  `string` — Required permanent, location-independent identifier for this Connector resource. Stable for the life of the resource. A CRID-addressed request returns that CRID; a resource-key request returns the canonical CRID.

* **`desired_state` (required)**

  `string`, possible values: `"on", "off"`

* **`publisher` (required)**

  `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

  - **`verified` (required)**

    `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

  - **`name`**

    `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

* **`resource_id` (required)**

  `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

* **`serving_epoch` (required)**

  `integer`, format: `int64`

**Example:**

```json
{
  "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
  "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
  "desired_state": "on",
  "serving_epoch": 0,
  "connection_state": "stopped",
  "created_at": "2026-03-01T09:00:00Z",
  "publisher": {
    "name": "Acme Docs",
    "verified": false
  }
}
```

### AllowedPasskey

- **Type:**`object`

A passkey you approved for a private resource.

- **`approved_at` (required)**

  `string`, format: `date-time` — When you approved the request.

- **`device_id` (required)**

  `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

- **`name` (required)**

  `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

**Example:**

```json
{
  "device_id": "k3wp-x2aq-7mzn-4tby",
  "name": "",
  "approved_at": ""
}
```

### AllowedPasskeyResponse

- **Type:**`object`

* **`data` (required)**

  `object` — A passkey you approved for a private resource.

  - **`approved_at` (required)**

    `string`, format: `date-time` — When you approved the request.

  - **`device_id` (required)**

    `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

  - **`name` (required)**

    `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "device_id": "k3wp-x2aq-7mzn-4tby",
    "name": "",
    "approved_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### AccessRequestData

- **Type:**`object`

A pending request for access to a private resource. It does not carry the request's 6-digit code, which you learn only from the requester.

- **`device_id` (required)**

  `string` — Identifies the requester's passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. A request from a passkey you approved before, on another resource, has the \`device\_id\` you saw then. Deny the request by this value.

- **`expires_at` (required)**

  `string`, format: `date-time` — When the request is dropped if you do not decide it.

- **`name` (required)**

  `string` — The name the requester typed. It is not verified. Display it as untrusted text.

- **`requested_at` (required)**

  `string`, format: `date-time`

**Example:**

```json
{
  "name": "",
  "device_id": "k3wp-x2aq-7mzn-4tby",
  "requested_at": "",
  "expires_at": ""
}
```

### AccessRequestListResponse

- **Type:**`object`

* **`data` (required)**

  `array`

  **Items:**

  - **`device_id` (required)**

    `string` — Identifies the requester's passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. A request from a passkey you approved before, on another resource, has the \`device\_id\` you saw then. Deny the request by this value.

  - **`expires_at` (required)**

    `string`, format: `date-time` — When the request is dropped if you do not decide it.

  - **`name` (required)**

    `string` — The name the requester typed. It is not verified. Display it as untrusted text.

  - **`requested_at` (required)**

    `string`, format: `date-time`

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": [
    {
      "name": "",
      "device_id": "k3wp-x2aq-7mzn-4tby",
      "requested_at": "",
      "expires_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### OwnerAccessRequestData

- **Type:**`object`

A pending access request, with the resource it is for. Every row carries the `crid` of that resource. It does not carry the request's 6-digit code, which you learn only from the requester.

- **`crid` (required)**

  `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

- **`device_id` (required)**

  `string` — Identifies the requester's passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`.

- **`expires_at` (required)**

  `string`, format: `date-time` — When the request is dropped if you do not decide it.

- **`name` (required)**

  `string` — The name the requester typed. It is not verified. Display it as untrusted text.

- **`requested_at` (required)**

  `string`, format: `date-time`

- **`resource_id` (required)**

  `string` — Public key of the resource the request is for, retained for compatibility. Prefer crid for later requests.

**Example:**

```json
{
  "name": "",
  "device_id": "k3wp-x2aq-7mzn-4tby",
  "requested_at": "",
  "expires_at": "",
  "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
  "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a"
}
```

### OwnerAccessRequestListResponse

- **Type:**`object`

* **`data` (required)**

  `array`

  **Items:**

  - **`crid` (required)**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`device_id` (required)**

    `string` — Identifies the requester's passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`.

  - **`expires_at` (required)**

    `string`, format: `date-time` — When the request is dropped if you do not decide it.

  - **`name` (required)**

    `string` — The name the requester typed. It is not verified. Display it as untrusted text.

  - **`requested_at` (required)**

    `string`, format: `date-time`

  - **`resource_id` (required)**

    `string` — Public key of the resource the request is for, retained for compatibility. Prefer crid for later requests.

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "name": "",
      "device_id": "k3wp-x2aq-7mzn-4tby",
      "requested_at": "",
      "expires_at": "",
      "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
      "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a"
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### ResourceSharingResponse

- **Type:**`object`

* **`data` (required)**

  `object`

  - **`connection_state` (required)**

    `string`, possible values: `"stopped", "connecting", "serving"` — GET derives this from authenticated Connector connections for the current lifecycle. A lifecycle mutation that rotates \`serving\_epoch\` reports \`connecting\` conservatively until a later GET observes a ready connection.

  - **`created_at` (required)**

    `string`, format: `date-time` — When this Connector resource was created.

  - **`crid` (required)**

    `string` — Required permanent, location-independent identifier for this Connector resource. Stable for the life of the resource. A CRID-addressed request returns that CRID; a resource-key request returns the canonical CRID.

  - **`desired_state` (required)**

    `string`, possible values: `"on", "off"`

  - **`publisher` (required)**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`resource_id` (required)**

    `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

  - **`serving_epoch` (required)**

    `integer`, format: `int64`

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "desired_state": "on",
    "serving_epoch": 0,
    "connection_state": "stopped",
    "created_at": "2026-03-01T09:00:00Z",
    "publisher": {
      "name": "Acme Docs",
      "verified": false
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### SessionData

- **Type:**`object`

* **`created_at`**

  `string`, format: `date-time`

* **`last_seen_at`**

  `string`, format: `date-time`

* **`qurl_id`**

  `string` — qURL display ID (q\_ prefix) that created this session

* **`session_id`**

  `string` — Unique session identifier

* **`src_ip`**

  `string` — Client IP that created the session

* **`user_agent`**

  `string` — Client user agent

**Example:**

```json
{
  "session_id": "",
  "qurl_id": "",
  "src_ip": "",
  "user_agent": "",
  "created_at": "",
  "last_seen_at": ""
}
```

### SessionListResponse

- **Type:**`object`

* **`data`**

  `array`

  **Items:**

  - **`created_at`**

    `string`, format: `date-time`

  - **`last_seen_at`**

    `string`, format: `date-time`

  - **`qurl_id`**

    `string` — qURL display ID (q\_ prefix) that created this session

  - **`session_id`**

    `string` — Unique session identifier

  - **`src_ip`**

    `string` — Client IP that created the session

  - **`user_agent`**

    `string` — Client user agent

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "session_id": "",
      "qurl_id": "",
      "src_ip": "",
      "user_agent": "",
      "created_at": "",
      "last_seen_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### SessionTerminateResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`terminated`**

    `integer` — Number of sessions terminated

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": {
    "terminated": 1
  },
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### ResourceResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`access_requests`**

    `boolean` — Whether someone who opens this private resource's link without access can ask you for it. Always present. Change it with \`PATCH /v1/resources/{id}\`.

  - **`alias`**

    `string | null` — Per-owner human-readable handle for the resource (e.g. \`dev-dashboard\`). \`null\` when no alias is set. Lookup via \`GET /v1/resources?alias={alias}\` (returns a 0- or 1-item list).

  - **`allowed_device_keys`**

    `array` — Public X25519 keys of the devices that can create links for this private resource, in canonical padded standard base64. Omitted when no devices are listed; only the owner can then create links for a private resource. The list has no effect unless the resource is private.

    **Items:**

    `string`

  - **`allowed_passkeys`**

    `array` — The passkeys you approved for this private resource, oldest approval first. Always present; empty when you approved none. A passkey is added by approving an access request and removed with \`DELETE /v1/resources/{id}/allowed-passkeys/{device\_id}\`.

    **Items:**

    - **`approved_at` (required)**

      `string`, format: `date-time` — When you approved the request.

    - **`device_id` (required)**

      `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

    - **`name` (required)**

      `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

  - **`connector_routing_id`**

    `string` — Opaque routing label the qURL Connector must use verbatim; do not derive it client-side.

  - **`consumer_access`**

    `object`

    - **`mode` (required)**

      `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

    - **`version` (required)**

      `integer`, format: `int64`

    - **`contract_id`**

      `string` — Required only for contract mode. Names the resource-specific contract.

  - **`consumer_access_version`**

    `integer`, format: `int64` — Monotonic consumer policy revision. Zero means no consumer policy has been set.

  - **`created_at`**

    `string`, format: `date-time`

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`desired_state`**

    `string`, possible values: `"on", "off"`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`knock_resource_id`**

    `string` — Internal use.

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`private`**

    `boolean`, default: `false` — When true, only the owner and the devices listed in \`allowed\_device\_keys\` can create links for this resource. A resource that is not private is public. Privacy is chosen when the resource is created (a new resource is private unless its creator asked for a public one) and cannot change afterward. Older resources can omit this field; they are public.

  - **`publisher`**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`qurl_count`**

    `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

  - **`serving_epoch`**

    `integer`, format: `int64` — Monotonic qURL Connector serving lifecycle. Zero means sharing has never been started.

  - **`session_duration_cap`**

    `integer` — Maximum session duration, in seconds, for qURLs on this resource. It is fixed when \`POST /v1/qurls\` first creates the resource. Later link creation and updates cannot exceed it (\`400 invalid\_session\_duration\`). Omitted session durations inherit the cap. Absent means there is no resource cap; the 1-second to 24-hour field limits still apply. \`POST /v1/resources\` creates a resource without a cap, and later link creation does not add one. To change a cap, register a different target URL, or revoke this resource and recreate it through \`POST /v1/qurls\` with the required \`session\_duration\`. Revocation blocks all existing links. A new resource receives a new CRID.

  - **`slug`**

    `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. Distinct from \`alias\`: slugs are immutable post-create, aliases are mutable. Lookup via \`GET /v1/resources?slug={slug}\` returns a 0- or 1-item active-resource list. See \`CreateResourceRequest.slug\` for format, uniqueness, and \`find\_or\_create\` semantics.

  - **`status`**

    `string`, possible values: `"active", "revoked"`

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `string` — The URL to protect. Optional — omitted (field absent from the JSON object, NOT serialized as null) when not applicable to this resource, or when the resource is owned by a connector.

  - **`tombstoned_at`**

    `string`, format: `date-time` — When the resource's lifecycle closed. Present only on tombstoned resources; absent on active ones. Consumers fetching a single resource by ID handle the tombstoned-row case via the 410 response on \`GET /v1/resources/{id}\` — the same timestamp surfaces in \`meta.tombstone.tombstoned\_at\`. List / alias-lookup (\`GET /v1/resources?alias=...\`) responses include this field on the resource itself so callers can distinguish tombstoned from active without a per-row probe.

  - **`type`**

    `string` — Resource type (see \`ResourceType\` for the full enum). Management-API read responses on connector-owned upload rows redact user-content resource fields (\`target\_url\`, \`description\`, \`tags\`, \`custom\_domain\`, \`alias\`, \`preserve\_host\`, \`qurl\_count\`, \`slug\`) and nested qURL summary fields (\`label\`, \`target\_path\`). List/detail responses include the row but omit those fields.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "consumer_access_version": 1,
    "consumer_access": {
      "mode": "keys",
      "version": 1,
      "contract_id": ""
    },
    "private": false,
    "allowed_device_keys": [
      ""
    ],
    "access_requests": true,
    "allowed_passkeys": [
      {
        "device_id": "k3wp-x2aq-7mzn-4tby",
        "name": "",
        "approved_at": ""
      }
    ],
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "type": "",
    "target_url": "",
    "knock_resource_id": "",
    "connector_routing_id": "",
    "status": "active",
    "desired_state": "on",
    "serving_epoch": 0,
    "description": "",
    "tags": [
      ""
    ],
    "custom_domain": null,
    "alias": null,
    "slug": "",
    "preserve_host": false,
    "session_duration_cap": 30,
    "qurl_count": 1,
    "created_at": "",
    "expires_at": "",
    "publisher": {
      "name": "Acme Docs",
      "verified": false
    },
    "tombstoned_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### ResourceDetailResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`qurls`**

    `array` — Unordered bounded preview of per-qURL summaries for this resource (maximum 100). This preview does not imply newest first or any stable ordering and is not active-first; an active qURL can be absent from the preview when earlier qURLs fill the cap. When present, \`resource.qurl\_count\` is the exact total; if omitted, callers must treat truncation as unknown. For the complete per-resource qURL list, use \`GET /v1/resources/{id}/qurls\`, which pages through every qURL with cursor-based pagination. For large resources, the preview and count are separate reads, so concurrent qURL changes can make the preview length and \`resource.qurl\_count\` briefly differ.

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

      - **`ai_agent_policy`**

        `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

        - **`allow_categories`**

          `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

        - **`block_all`**

          `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

        - **`deny_categories`**

          `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`geo_allowlist`**

        `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`geo_denylist`**

        `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`ip_allowlist`**

        `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`ip_denylist`**

        `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`user_agent_allow_regex`**

        `string` — Regular expression to allow matching user agents (max 256 characters)

      - **`user_agent_deny_regex`**

        `string` — Regular expression to deny matching user agents (max 256 characters)

    - **`created_at`**

      `string`, format: `date-time`

    - **`expires_at`**

      `string`, format: `date-time`

    - **`label`**

      `string`

    - **`max_sessions`**

      `integer`

    - **`one_time_use`**

      `boolean`

    - **`qurl_id`**

      `string`

    - **`qurl_site`**

      `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

    - **`session_duration`**

      `integer` — Session lifetime in seconds (0 = server default)

    - **`status`**

      `string`, possible values: `"active", "consumed", "expired", "revoked"`

    - **`target_path`**

      `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

    - **`use_count`**

      `integer`

  - **`resource`**

    `object`

    - **`access_requests`**

      `boolean` — Whether someone who opens this private resource's link without access can ask you for it. Always present. Change it with \`PATCH /v1/resources/{id}\`.

    - **`alias`**

      `string | null` — Per-owner human-readable handle for the resource (e.g. \`dev-dashboard\`). \`null\` when no alias is set. Lookup via \`GET /v1/resources?alias={alias}\` (returns a 0- or 1-item list).

    - **`allowed_device_keys`**

      `array` — Public X25519 keys of the devices that can create links for this private resource, in canonical padded standard base64. Omitted when no devices are listed; only the owner can then create links for a private resource. The list has no effect unless the resource is private.

      **Items:**

      `string`

    - **`allowed_passkeys`**

      `array` — The passkeys you approved for this private resource, oldest approval first. Always present; empty when you approved none. A passkey is added by approving an access request and removed with \`DELETE /v1/resources/{id}/allowed-passkeys/{device\_id}\`.

      **Items:**

      - **`approved_at` (required)**

        `string`, format: `date-time` — When you approved the request.

      - **`device_id` (required)**

        `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

      - **`name` (required)**

        `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

    - **`connector_routing_id`**

      `string` — Opaque routing label the qURL Connector must use verbatim; do not derive it client-side.

    - **`consumer_access`**

      `object`

      - **`mode` (required)**

        `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

      - **`version` (required)**

        `integer`, format: `int64`

      - **`contract_id`**

        `string` — Required only for contract mode. Names the resource-specific contract.

    - **`consumer_access_version`**

      `integer`, format: `int64` — Monotonic consumer policy revision. Zero means no consumer policy has been set.

    - **`created_at`**

      `string`, format: `date-time`

    - **`crid`**

      `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

    - **`custom_domain`**

      `string | null`

    - **`description`**

      `string`

    - **`desired_state`**

      `string`, possible values: `"on", "off"`

    - **`expires_at`**

      `string`, format: `date-time`

    - **`knock_resource_id`**

      `string` — Internal use.

    - **`preserve_host`**

      `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

    - **`private`**

      `boolean`, default: `false` — When true, only the owner and the devices listed in \`allowed\_device\_keys\` can create links for this resource. A resource that is not private is public. Privacy is chosen when the resource is created (a new resource is private unless its creator asked for a public one) and cannot change afterward. Older resources can omit this field; they are public.

    - **`publisher`**

      `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

      - **`verified` (required)**

        `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

      - **`name`**

        `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

    - **`qurl_count`**

      `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

    - **`resource_id`**

      `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

    - **`serving_epoch`**

      `integer`, format: `int64` — Monotonic qURL Connector serving lifecycle. Zero means sharing has never been started.

    - **`session_duration_cap`**

      `integer` — Maximum session duration, in seconds, for qURLs on this resource. It is fixed when \`POST /v1/qurls\` first creates the resource. Later link creation and updates cannot exceed it (\`400 invalid\_session\_duration\`). Omitted session durations inherit the cap. Absent means there is no resource cap; the 1-second to 24-hour field limits still apply. \`POST /v1/resources\` creates a resource without a cap, and later link creation does not add one. To change a cap, register a different target URL, or revoke this resource and recreate it through \`POST /v1/qurls\` with the required \`session\_duration\`. Revocation blocks all existing links. A new resource receives a new CRID.

    - **`slug`**

      `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. Distinct from \`alias\`: slugs are immutable post-create, aliases are mutable. Lookup via \`GET /v1/resources?slug={slug}\` returns a 0- or 1-item active-resource list. See \`CreateResourceRequest.slug\` for format, uniqueness, and \`find\_or\_create\` semantics.

    - **`status`**

      `string`, possible values: `"active", "revoked"`

    - **`tags`**

      `array`

      **Items:**

      `string`

    - **`target_url`**

      `string` — The URL to protect. Optional — omitted (field absent from the JSON object, NOT serialized as null) when not applicable to this resource, or when the resource is owned by a connector.

    - **`tombstoned_at`**

      `string`, format: `date-time` — When the resource's lifecycle closed. Present only on tombstoned resources; absent on active ones. Consumers fetching a single resource by ID handle the tombstoned-row case via the 410 response on \`GET /v1/resources/{id}\` — the same timestamp surfaces in \`meta.tombstone.tombstoned\_at\`. List / alias-lookup (\`GET /v1/resources?alias=...\`) responses include this field on the resource itself so callers can distinguish tombstoned from active without a per-row probe.

    - **`type`**

      `string` — Resource type (see \`ResourceType\` for the full enum). Management-API read responses on connector-owned upload rows redact user-content resource fields (\`target\_url\`, \`description\`, \`tags\`, \`custom\_domain\`, \`alias\`, \`preserve\_host\`, \`qurl\_count\`, \`slug\`) and nested qURL summary fields (\`label\`, \`target\_path\`). List/detail responses include the row but omit those fields.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "resource": {
      "consumer_access_version": 1,
      "consumer_access": {
        "mode": "keys",
        "version": 1,
        "contract_id": ""
      },
      "private": false,
      "allowed_device_keys": [
        ""
      ],
      "access_requests": true,
      "allowed_passkeys": [
        {
          "device_id": "k3wp-x2aq-7mzn-4tby",
          "name": "",
          "approved_at": ""
        }
      ],
      "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
      "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
      "type": "",
      "target_url": "",
      "knock_resource_id": "",
      "connector_routing_id": "",
      "status": "active",
      "desired_state": "on",
      "serving_epoch": 0,
      "description": "",
      "tags": [
        ""
      ],
      "custom_domain": null,
      "alias": null,
      "slug": "",
      "preserve_host": false,
      "session_duration_cap": 30,
      "qurl_count": 1,
      "created_at": "",
      "expires_at": "",
      "publisher": {
        "name": "Acme Docs",
        "verified": false
      },
      "tombstoned_at": ""
    },
    "qurls": [
      {
        "qurl_id": "",
        "label": "",
        "status": "active",
        "one_time_use": true,
        "max_sessions": 1,
        "session_duration": 1,
        "use_count": 1,
        "qurl_site": "",
        "target_path": "",
        "created_at": "",
        "expires_at": "",
        "access_policy": {
          "ip_allowlist": [
            "192.168.1.0/24",
            "10.0.0.1"
          ],
          "ip_denylist": [
            ""
          ],
          "geo_allowlist": [
            "US",
            "CA",
            "GB"
          ],
          "geo_denylist": [
            "CN",
            "RU"
          ],
          "user_agent_allow_regex": "^Mozilla.*",
          "user_agent_deny_regex": ".*bot.*",
          "ai_agent_policy": {
            "block_all": false,
            "deny_categories": [
              "gptbot",
              "commoncrawl",
              "bytedance"
            ],
            "allow_categories": [
              "claude",
              "chatgpt",
              "perplexity"
            ]
          }
        }
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### ResourceListResponse

- **Type:**`object`

* **`data`**

  `array`

  **Items:**

  - **`access_requests`**

    `boolean` — Whether someone who opens this private resource's link without access can ask you for it. Always present. Change it with \`PATCH /v1/resources/{id}\`.

  - **`alias`**

    `string | null` — Per-owner human-readable handle for the resource (e.g. \`dev-dashboard\`). \`null\` when no alias is set. Lookup via \`GET /v1/resources?alias={alias}\` (returns a 0- or 1-item list).

  - **`allowed_device_keys`**

    `array` — Public X25519 keys of the devices that can create links for this private resource, in canonical padded standard base64. Omitted when no devices are listed; only the owner can then create links for a private resource. The list has no effect unless the resource is private.

    **Items:**

    `string`

  - **`allowed_passkeys`**

    `array` — The passkeys you approved for this private resource, oldest approval first. Always present; empty when you approved none. A passkey is added by approving an access request and removed with \`DELETE /v1/resources/{id}/allowed-passkeys/{device\_id}\`.

    **Items:**

    - **`approved_at` (required)**

      `string`, format: `date-time` — When you approved the request.

    - **`device_id` (required)**

      `string` — Identifies the passkey, in the form \`xxxx-xxxx-xxxx-xxxx\`. It is the same for one passkey on every resource and in every access request, and it is not a secret.

    - **`name` (required)**

      `string` — The name the requester typed when they asked for access. It is not verified. Display it as untrusted text.

  - **`connector_routing_id`**

    `string` — Opaque routing label the qURL Connector must use verbatim; do not derive it client-side.

  - **`consumer_access`**

    `object`

    - **`mode` (required)**

      `string`, possible values: `"keys", "invite_only", "email_capture", "review", "contract"`

    - **`version` (required)**

      `integer`, format: `int64`

    - **`contract_id`**

      `string` — Required only for contract mode. Names the resource-specific contract.

  - **`consumer_access_version`**

    `integer`, format: `int64` — Monotonic consumer policy revision. Zero means no consumer policy has been set.

  - **`created_at`**

    `string`, format: `date-time`

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`desired_state`**

    `string`, possible values: `"on", "off"`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`knock_resource_id`**

    `string` — Internal use.

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`private`**

    `boolean`, default: `false` — When true, only the owner and the devices listed in \`allowed\_device\_keys\` can create links for this resource. A resource that is not private is public. Privacy is chosen when the resource is created (a new resource is private unless its creator asked for a public one) and cannot change afterward. Older resources can omit this field; they are public.

  - **`publisher`**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`qurl_count`**

    `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer crid for later requests.

  - **`serving_epoch`**

    `integer`, format: `int64` — Monotonic qURL Connector serving lifecycle. Zero means sharing has never been started.

  - **`session_duration_cap`**

    `integer` — Maximum session duration, in seconds, for qURLs on this resource. It is fixed when \`POST /v1/qurls\` first creates the resource. Later link creation and updates cannot exceed it (\`400 invalid\_session\_duration\`). Omitted session durations inherit the cap. Absent means there is no resource cap; the 1-second to 24-hour field limits still apply. \`POST /v1/resources\` creates a resource without a cap, and later link creation does not add one. To change a cap, register a different target URL, or revoke this resource and recreate it through \`POST /v1/qurls\` with the required \`session\_duration\`. Revocation blocks all existing links. A new resource receives a new CRID.

  - **`slug`**

    `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. Distinct from \`alias\`: slugs are immutable post-create, aliases are mutable. Lookup via \`GET /v1/resources?slug={slug}\` returns a 0- or 1-item active-resource list. See \`CreateResourceRequest.slug\` for format, uniqueness, and \`find\_or\_create\` semantics.

  - **`status`**

    `string`, possible values: `"active", "revoked"`

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `string` — The URL to protect. Optional — omitted (field absent from the JSON object, NOT serialized as null) when not applicable to this resource, or when the resource is owned by a connector.

  - **`tombstoned_at`**

    `string`, format: `date-time` — When the resource's lifecycle closed. Present only on tombstoned resources; absent on active ones. Consumers fetching a single resource by ID handle the tombstoned-row case via the 410 response on \`GET /v1/resources/{id}\` — the same timestamp surfaces in \`meta.tombstone.tombstoned\_at\`. List / alias-lookup (\`GET /v1/resources?alias=...\`) responses include this field on the resource itself so callers can distinguish tombstoned from active without a per-row probe.

  - **`type`**

    `string` — Resource type (see \`ResourceType\` for the full enum). Management-API read responses on connector-owned upload rows redact user-content resource fields (\`target\_url\`, \`description\`, \`tags\`, \`custom\_domain\`, \`alias\`, \`preserve\_host\`, \`qurl\_count\`, \`slug\`) and nested qURL summary fields (\`label\`, \`target\_path\`). List/detail responses include the row but omit those fields.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "consumer_access_version": 1,
      "consumer_access": {
        "mode": "keys",
        "version": 1,
        "contract_id": ""
      },
      "private": false,
      "allowed_device_keys": [
        ""
      ],
      "access_requests": true,
      "allowed_passkeys": [
        {
          "device_id": "k3wp-x2aq-7mzn-4tby",
          "name": "",
          "approved_at": ""
        }
      ],
      "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
      "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
      "type": "",
      "target_url": "",
      "knock_resource_id": "",
      "connector_routing_id": "",
      "status": "active",
      "desired_state": "on",
      "serving_epoch": 0,
      "description": "",
      "tags": [
        ""
      ],
      "custom_domain": null,
      "alias": null,
      "slug": "",
      "preserve_host": false,
      "session_duration_cap": 30,
      "qurl_count": 1,
      "created_at": "",
      "expires_at": "",
      "publisher": {
        "name": "Acme Docs",
        "verified": false
      },
      "tombstoned_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### ConnectorInstallationStats

- **Type:**`object`

* **`accesses_24h` (required)**

  `integer`, format: `int64` — Successful accesses in the last 24 hours.

* **`accesses_7d` (required)**

  `integer`, format: `int64` — Successful accesses in the last 7 days.

* **`errors_24h` (required)**

  `integer`, format: `int64` — Failed connector actions in the last 24 hours.

* **`qurls` (required)**

  `integer`, format: `int64` — Number of active qURLs associated with this installation.

* **`resources` (required)**

  `integer`, format: `int64` — Number of protected resources associated with this installation.

**Example:**

```json
{
  "resources": 1,
  "qurls": 1,
  "accesses_24h": 1,
  "accesses_7d": 1,
  "errors_24h": 1
}
```

### ConnectorInstallationCapabilities

- **Type:**`object`

* **`configure` (required)**

  `boolean`

* **`disconnect` (required)**

  `boolean`

* **`reauth` (required)**

  `boolean`

* **`view_activity` (required)**

  `boolean`

**Example:**

```json
{
  "configure": true,
  "disconnect": true,
  "reauth": true,
  "view_activity": true
}
```

### ConnectorInstallationSummary

- **Type:**`object`

* **`capabilities` (required)**

  `object`

  - **`configure` (required)**

    `boolean`

  - **`disconnect` (required)**

    `boolean`

  - **`reauth` (required)**

    `boolean`

  - **`view_activity` (required)**

    `boolean`

* **`installation_id` (required)**

  `string` — Stable identifier for this connector installation.

* **`installed_at` (required)**

  `string`, format: `date-time`

* **`label` (required)**

  `string` — Human-readable label for this installation.

* **`plugin_id` (required)**

  `string` — Connector plugin identifier.

* **`stats` (required)**

  `object`

  - **`accesses_24h` (required)**

    `integer`, format: `int64` — Successful accesses in the last 24 hours.

  - **`accesses_7d` (required)**

    `integer`, format: `int64` — Successful accesses in the last 7 days.

  - **`errors_24h` (required)**

    `integer`, format: `int64` — Failed connector actions in the last 24 hours.

  - **`qurls` (required)**

    `integer`, format: `int64` — Number of active qURLs associated with this installation.

  - **`resources` (required)**

    `integer`, format: `int64` — Number of protected resources associated with this installation.

* **`status` (required)**

  `string`, possible values: `"active", "degraded", "disconnected", "needs_reauth"`

* **`subject_display_name` (required)**

  `string` — Display name for the connected workspace, guild, bucket, or account.

* **`subject_kind` (required)**

  `string` — Connector-owned subject type.

* **`last_activity_at`**

  `string | null`, format: `date-time`

**Example:**

```json
{
  "installation_id": "",
  "plugin_id": "slack",
  "label": "Engineering Slack",
  "subject_kind": "slack_workspace",
  "subject_display_name": "LayerV Engineering",
  "status": "active",
  "installed_at": "",
  "last_activity_at": null,
  "stats": {
    "resources": 1,
    "qurls": 1,
    "accesses_24h": 1,
    "accesses_7d": 1,
    "errors_24h": 1
  },
  "capabilities": {
    "configure": true,
    "disconnect": true,
    "reauth": true,
    "view_activity": true
  }
}
```

### ConnectorDevice

- **Type:**`object`

* **`agent_id` (required)**

  `string`

* **`created_at` (required)**

  `string`, format: `date-time`

* **`credential_status` (required)**

  `string`, possible values: `"active", "revoked", "registering"`

* **`display_name` (required)**

  `string`

* **`device_api_key_id`**

  `string` — Current device credential identifier, when registration has completed.

* **`hostname`**

  `string`

* **`last_active_at`**

  `string`, format: `date-time` — Most recent recorded native device activity; omitted when unavailable. This is not an online indicator.

**Example:**

```json
{
  "agent_id": "",
  "display_name": "",
  "hostname": "",
  "created_at": "",
  "last_active_at": "",
  "credential_status": "active",
  "device_api_key_id": ""
}
```

### ConnectorDeviceUsage

- **Type:**`object`

* **`active_device_credentials` (required)**

  `integer`, format: `int64`

* **`device_count` (required)**

  `integer`, format: `int64` — Assigned device slots, including devices whose access was revoked.

* **`device_credential_limit` (required)**

  `integer`

* **`device_limit` (required)**

  `integer`

**Example:**

```json
{
  "device_count": 1,
  "device_limit": 1,
  "active_device_credentials": 1,
  "device_credential_limit": 1
}
```

### ConnectorDeviceListResponse

- **Type:**`object`

* **`data` (required)**

  `array`

  **Items:**

  - **`agent_id` (required)**

    `string`

  - **`created_at` (required)**

    `string`, format: `date-time`

  - **`credential_status` (required)**

    `string`, possible values: `"active", "revoked", "registering"`

  - **`display_name` (required)**

    `string`

  - **`device_api_key_id`**

    `string` — Current device credential identifier, when registration has completed.

  - **`hostname`**

    `string`

  - **`last_active_at`**

    `string`, format: `date-time` — Most recent recorded native device activity; omitted when unavailable. This is not an online indicator.

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

* **`usage` (required)**

  `object`

  - **`active_device_credentials` (required)**

    `integer`, format: `int64`

  - **`device_count` (required)**

    `integer`, format: `int64` — Assigned device slots, including devices whose access was revoked.

  - **`device_credential_limit` (required)**

    `integer`

  - **`device_limit` (required)**

    `integer`

**Example:**

```json
{
  "data": [
    {
      "agent_id": "",
      "display_name": "",
      "hostname": "",
      "created_at": "",
      "last_active_at": "",
      "credential_status": "active",
      "device_api_key_id": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  },
  "usage": {
    "device_count": 1,
    "device_limit": 1,
    "active_device_credentials": 1,
    "device_credential_limit": 1
  }
}
```

### ConnectorDeviceResponse

- **Type:**`object`

* **`data` (required)**

  `object`

  - **`agent_id` (required)**

    `string`

  - **`created_at` (required)**

    `string`, format: `date-time`

  - **`credential_status` (required)**

    `string`, possible values: `"active", "revoked", "registering"`

  - **`display_name` (required)**

    `string`

  - **`device_api_key_id`**

    `string` — Current device credential identifier, when registration has completed.

  - **`hostname`**

    `string`

  - **`last_active_at`**

    `string`, format: `date-time` — Most recent recorded native device activity; omitted when unavailable. This is not an online indicator.

**Example:**

```json
{
  "data": {
    "agent_id": "",
    "display_name": "",
    "hostname": "",
    "created_at": "",
    "last_active_at": "",
    "credential_status": "active",
    "device_api_key_id": ""
  }
}
```

### RenameConnectorDeviceRequest

- **Type:**`object`

* **`display_name` (required)**

  `string` — A device label without leading or trailing whitespace or control characters. It survives credential recovery.

**Example:**

```json
{
  "display_name": ""
}
```

### ConnectorInstallationListResponse

- **Type:**`object`

* **`data` (required)**

  `array`

  **Items:**

  - **`capabilities` (required)**

    `object`

    - **`configure` (required)**

      `boolean`

    - **`disconnect` (required)**

      `boolean`

    - **`reauth` (required)**

      `boolean`

    - **`view_activity` (required)**

      `boolean`

  - **`installation_id` (required)**

    `string` — Stable identifier for this connector installation.

  - **`installed_at` (required)**

    `string`, format: `date-time`

  - **`label` (required)**

    `string` — Human-readable label for this installation.

  - **`plugin_id` (required)**

    `string` — Connector plugin identifier.

  - **`stats` (required)**

    `object`

    - **`accesses_24h` (required)**

      `integer`, format: `int64` — Successful accesses in the last 24 hours.

    - **`accesses_7d` (required)**

      `integer`, format: `int64` — Successful accesses in the last 7 days.

    - **`errors_24h` (required)**

      `integer`, format: `int64` — Failed connector actions in the last 24 hours.

    - **`qurls` (required)**

      `integer`, format: `int64` — Number of active qURLs associated with this installation.

    - **`resources` (required)**

      `integer`, format: `int64` — Number of protected resources associated with this installation.

  - **`status` (required)**

    `string`, possible values: `"active", "degraded", "disconnected", "needs_reauth"`

  - **`subject_display_name` (required)**

    `string` — Display name for the connected workspace, guild, bucket, or account.

  - **`subject_kind` (required)**

    `string` — Connector-owned subject type.

  - **`last_activity_at`**

    `string | null`, format: `date-time`

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "installation_id": "",
      "plugin_id": "slack",
      "label": "Engineering Slack",
      "subject_kind": "slack_workspace",
      "subject_display_name": "LayerV Engineering",
      "status": "active",
      "installed_at": "",
      "last_activity_at": null,
      "stats": {
        "resources": 1,
        "qurls": 1,
        "accesses_24h": 1,
        "accesses_7d": 1,
        "errors_24h": 1
      },
      "capabilities": {
        "configure": true,
        "disconnect": true,
        "reauth": true,
        "view_activity": true
      }
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### UpdateQurlRequest

- **Type:**`object`

Updates resource metadata or expiration. Provide at least one field. To change expiration, use `extend_by` or `expires_at`, not both.

- **`description`**

  `string` — Replace the resource description. Pass an empty string to clear.

- **`expires_at`**

  `string`, format: `date-time` — Absolute expiration time. Mutually exclusive with extend\_by. Must be in the future and within 30 days from now.

- **`extend_by`**

  `string` — Duration to extend by (e.g., "24h", "7d", "1w"). Minimum: 1 minute. Maximum: 30 days. Mutually exclusive with expires\_at.

- **`tags`**

  `array` — Replace all tags on this resource. Tags are lowercased and trimmed server-side. Duplicates are silently removed. Pass an empty array to clear all tags.

  **Items:**

  `string`

**Example:**

```json
{
  "extend_by": "1h",
  "expires_at": "",
  "tags": [
    ""
  ],
  "description": ""
}
```

### MintLinkRequest

- **Type:**`object`

Creates another link for an existing resource. Send `expires_in` or `expires_at`, not both. If neither is set, the service chooses the default lifetime, including for qURL Connectors. The resource's expiration can shorten the result. Unlinked anonymous devices are also capped at 24 hours; use the returned `expires_at` as the effective expiry. For new integrations, prefer `POST /v1/resources/{id}/qurls`.

- **`access_policy`**

  `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

  - **`ai_agent_policy`**

    `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

    - **`allow_categories`**

      `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`block_all`**

      `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

    - **`deny_categories`**

      `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

      **Items:**

      `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

  - **`geo_allowlist`**

    `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`geo_denylist`**

    `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

    **Items:**

    `string`

  - **`ip_allowlist`**

    `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`ip_denylist`**

    `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

    **Items:**

    `string`

  - **`user_agent_allow_regex`**

    `string` — Regular expression to allow matching user agents (max 256 characters)

  - **`user_agent_deny_regex`**

    `string` — Regular expression to deny matching user agents (max 256 characters)

- **`expires_at`**

  `string`, format: `date-time` — Absolute expiration timestamp. Must be in the future and within 30 days from now. Mutually exclusive with expires\_in.

- **`expires_in`**

  `string` — Duration until expiration. Supports: minutes (5m), hours (24h), days (7d), weeks (1w). Minimum: 1 minute. Maximum: 30 days.

- **`label`**

  `string` — Human-readable label identifying who this qURL is for

- **`max_sessions`**

  `integer`, default: `0` — Maximum concurrent sessions for this qURL token (0 = unlimited)

- **`one_time_use`**

  `boolean` — Whether the link is consumed by its first successful use. Explicit \`true\` or \`false\` takes precedence over the resource defaults. When omitted, URL and integration-owned upload links use \`false\`. qURL Connector links use \`true\` unless \`max\_sessions\` is greater than 1. For a reusable Connector link, explicitly send \`one\_time\_use: false\`, including when \`max\_sessions\` is \`0\` (unlimited).

- **`session_duration`**

  `string` — How long a session permits new requests after the recipient first reaches the content. Minimum: 1 second. Maximum: 24 hours. If the parent resource was created with a \`session\_duration\` (see \`ResourceData.session\_duration\_cap\`), values exceeding that cap are rejected with HTTP 400 \`invalid\_session\_duration\`. Omitting this field on a capped resource inherits the cap; without a cap, qURL Connectors use 1 hour and other resource types use the service default. New requests are denied at session expiry. Active requests, streams, and WebSockets are canceled at that deadline. See \`CreateQurlRequest.session\_duration\` → \*\*Timing precision\*\* for the full semantics.

- **`target_path`**

  `string` — Destination path and optional query for a qURL Connector link. For example, \`/view/abc?download=1\` redirects to that path on the resource's \`qurl.site\` host. Omit this field to use the host's root. Only qURL Connector resources (\`type=tunnel\`) accept it. Maximum: 2048 UTF-8 bytes. \*\*Access scope:\*\* The path permits itself and descendants at a segment boundary. \`/view/abc\` permits \`/view/abc/thumbnail\`, but not \`/view/abcd\`. The query does not restrict access. A scoped link works only through the resource host; access through a custom domain is denied. The Connector must also authorize the path before using it to look up an object. \*\*Path rules:\*\* Start with one \`/\`. Use canonical, unescaped ASCII. A single trailing slash is allowed. Do not use \`//\`, percent escapes, semicolons, empty interior segments, \`.\` or \`..\` segments, exclamation marks, parentheses, asterisks, apostrophes, a scheme, host, backslash, fragment, whitespace, or control characters. Use opaque route keys for objects; keep user filenames in metadata. Clients must preserve the accepted path bytes. Percent-encoded alternatives are denied. \*\*Query rules:\*\* Use ASCII letters, digits, \`.\_\~!$&()\*+,;=:@/?-\`, and well-formed percent escapes. Percent-encode other characters. Literal apostrophes are not accepted. Query bytes are preserved, including semicolons and percent escapes. \*\*Errors:\*\* JSON null and non-string values return \`400 validation\_error\`. Empty strings, oversized or malformed paths, and use on other resource types return \`400 invalid\_target\_path\`.

**Example:**

```json
{
  "expires_in": "5m",
  "expires_at": "",
  "label": "Alice from Acme",
  "one_time_use": true,
  "max_sessions": 0,
  "session_duration": "1h",
  "access_policy": {
    "ip_allowlist": [
      "192.168.1.0/24",
      "10.0.0.1"
    ],
    "ip_denylist": [
      ""
    ],
    "geo_allowlist": [
      "US",
      "CA",
      "GB"
    ],
    "geo_denylist": [
      "CN",
      "RU"
    ],
    "user_agent_allow_regex": "^Mozilla.*",
    "user_agent_deny_regex": ".*bot.*",
    "ai_agent_policy": {
      "block_all": false,
      "deny_categories": [
        "gptbot",
        "commoncrawl",
        "bytedance"
      ],
      "allow_categories": [
        "claude",
        "chatgpt",
        "perplexity"
      ]
    }
  },
  "target_path": "/view/9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}
```

### AccessPolicy

- **Type:**`object`

Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

- **`ai_agent_policy`**

  `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

  - **`allow_categories`**

    `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

    **Items:**

    `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

  - **`block_all`**

    `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

  - **`deny_categories`**

    `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

    **Items:**

    `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

- **`geo_allowlist`**

  `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

  **Items:**

  `string`

- **`geo_denylist`**

  `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

  **Items:**

  `string`

- **`ip_allowlist`**

  `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

  **Items:**

  `string`

- **`ip_denylist`**

  `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

  **Items:**

  `string`

- **`user_agent_allow_regex`**

  `string` — Regular expression to allow matching user agents (max 256 characters)

- **`user_agent_deny_regex`**

  `string` — Regular expression to deny matching user agents (max 256 characters)

**Example:**

```json
{
  "ip_allowlist": [
    "192.168.1.0/24",
    "10.0.0.1"
  ],
  "ip_denylist": [
    ""
  ],
  "geo_allowlist": [
    "US",
    "CA",
    "GB"
  ],
  "geo_denylist": [
    "CN",
    "RU"
  ],
  "user_agent_allow_regex": "^Mozilla.*",
  "user_agent_deny_regex": ".*bot.*",
  "ai_agent_policy": {
    "block_all": false,
    "deny_categories": [
      "gptbot",
      "commoncrawl",
      "bytedance"
    ],
    "allow_categories": [
      "claude",
      "chatgpt",
      "perplexity"
    ]
  }
}
```

### AIAgentCategory

- **Type:**`string`

A well-known AI agent category identifier

**Example:**

### AIAgentPolicy

- **Type:**`object`

Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it.

IP and country rules are checked first. This policy is checked next, followed by `user_agent_deny_regex` and `user_agent_allow_regex`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here.

Block all recognized AI agents:

```json
{"block_all": true}
```

Block specific crawlers:

```json
{"deny_categories": ["gptbot", "commoncrawl"]}
```

Allow only Claude and Perplexity among recognized AI agents:

```json
{"allow_categories": ["claude", "perplexity"]}
```

If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

- **`allow_categories`**

  `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

  **Items:**

  `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

- **`block_all`**

  `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

- **`deny_categories`**

  `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

  **Items:**

  `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

**Example:**

```json
{
  "block_all": false,
  "deny_categories": [
    "gptbot",
    "commoncrawl",
    "bytedance"
  ],
  "allow_categories": [
    "claude",
    "chatgpt",
    "perplexity"
  ]
}
```

### QurlData

- **Type:**`object`

Resource details returned by the `/v1/qurls` compatibility operations. Use `crid` to identify the resource and `qurl_id` to identify an individual link. The `qurls` array (present on single-get responses) is an unordered bounded preview (maximum 100) of per-qURL details including access policy and session limits. On list responses, only `qurl_count` is populated.

- **`created_at`**

  `string`, format: `date-time` — When the resource was created.

- **`crid`**

  `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

- **`custom_domain`**

  `string | null` — Custom domain associated with this resource

- **`description`**

  `string` — Human-readable description

- **`expires_at`**

  `string`, format: `date-time` — When the resource expires. Each qURL can have its own earlier expiration.

- **`qurl_count`**

  `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

- **`qurl_site`**

  `string`, format: `uri` — Routing URL for the resource. It does not grant access by itself. Share a qURL access link with the recipient. Do not extract an identifier from this hostname.

- **`qurls`**

  `array` — Unordered bounded preview of per-qURL details (maximum 100). Best-effort on single-get and omitted on list, preview lookup failure, or redacted resource types. This preview does not imply newest first or any stable ordering and is not active-first; an active qURL can be absent from the preview when earlier qURLs fill the cap. When present, \`qurl\_count\` is the exact total; if omitted, callers must treat truncation as unknown. For the complete per-resource qURL list, use \`GET /v1/resources/{id}/qurls\`, which pages through every qURL with cursor-based pagination. For large resources, the preview and count are separate reads, so concurrent qURL changes can make the preview length and \`qurl\_count\` briefly differ.

  **Items:**

  - **`access_policy`**

    `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

    - **`ai_agent_policy`**

      `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

      - **`allow_categories`**

        `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`block_all`**

        `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

      - **`deny_categories`**

        `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`geo_allowlist`**

      `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`geo_denylist`**

      `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`ip_allowlist`**

      `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`ip_denylist`**

      `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`user_agent_allow_regex`**

      `string` — Regular expression to allow matching user agents (max 256 characters)

    - **`user_agent_deny_regex`**

      `string` — Regular expression to deny matching user agents (max 256 characters)

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`label`**

    `string`

  - **`max_sessions`**

    `integer`

  - **`one_time_use`**

    `boolean`

  - **`qurl_id`**

    `string`

  - **`qurl_site`**

    `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

  - **`session_duration`**

    `integer` — Session lifetime in seconds (0 = server default)

  - **`status`**

    `string`, possible values: `"active", "consumed", "expired", "revoked"`

  - **`target_path`**

    `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

  - **`use_count`**

    `integer`

- **`resource_id`**

  `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

- **`slug`**

  `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. See \`ResourceData.slug\` for the full description — the field carries identical semantics on both response schemas.

- **`status`**

  `string`, possible values: `"active", "revoked"` — Current lifecycle status. Computed at response time — resources past their expires\_at are reported as "expired" even if not explicitly revoked.

- **`tags`**

  `array` — Tags for categorization and filtering

  **Items:**

  `string`

- **`target_url`**

  `string`, format: `uri` — The protected backend URL. Optional — omitted (field absent from the JSON object, NOT serialized as null) when the resource is owned by a connector.

**Example:**

```json
{
  "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
  "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
  "target_url": "",
  "status": "active",
  "created_at": "",
  "expires_at": "",
  "description": "",
  "tags": [
    ""
  ],
  "qurl_site": "",
  "custom_domain": null,
  "slug": "",
  "qurl_count": 1,
  "qurls": [
    {
      "qurl_id": "",
      "label": "",
      "status": "active",
      "one_time_use": true,
      "max_sessions": 1,
      "session_duration": 1,
      "use_count": 1,
      "qurl_site": "",
      "target_path": "",
      "created_at": "",
      "...": "[Additional Properties Truncated]"
    }
  ]
}
```

### CreateQurlData

- **Type:**`object`

Response data for qURL creation

- **`branded_domain`**

  `string` — Custom hostname to display as link text. Keep \`qurl\_link\` as the destination. See \`MintLinkData.branded\_domain\` for domain status requirements. An idempotent retry returns the original value. Check the resource and domain again if you need the current domain status.

- **`crid`**

  `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

- **`expires_at`**

  `string`, format: `date-time`

- **`label`**

  `string` — Human-readable label for this qURL

- **`qurl_id`**

  `string` — Identifier for this qURL. Save it to revoke or update this link, or match its webhook events.

- **`qurl_link`**

  `string`, format: `uri` — Access link to share with the recipient. Store it securely; later GET requests do not return it. Exact request retries can replay the creation response within the replay window.

- **`qurl_site`**

  `string`, format: `uri` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

- **`resource_id`**

  `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

- **`target_path`**

  `string` — Redirect destination and authorization scope for this new qURL on a qURL Connector resource. Omitted when the new qURL uses the bare origin. Each create call makes an independent qURL; omission affects only that qURL.

- **`type`**

  `string` — Resource type — echoes the type from the create request.

**Example:**

```json
{
  "qurl_id": "q_3a7f2c8e91b",
  "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
  "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
  "qurl_link": "",
  "branded_domain": "",
  "qurl_site": "",
  "expires_at": "",
  "target_path": "",
  "label": "",
  "type": ""
}
```

### QurlResponse

- **Type:**`object`

* **`data`**

  `object` — Resource details returned by the \`/v1/qurls\` compatibility operations. Use \`crid\` to identify the resource and \`qurl\_id\` to identify an individual link. The \`qurls\` array (present on single-get responses) is an unordered bounded preview (maximum 100) of per-qURL details including access policy and session limits. On list responses, only \`qurl\_count\` is populated.

  - **`created_at`**

    `string`, format: `date-time` — When the resource was created.

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`custom_domain`**

    `string | null` — Custom domain associated with this resource

  - **`description`**

    `string` — Human-readable description

  - **`expires_at`**

    `string`, format: `date-time` — When the resource expires. Each qURL can have its own earlier expiration.

  - **`qurl_count`**

    `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

  - **`qurl_site`**

    `string`, format: `uri` — Routing URL for the resource. It does not grant access by itself. Share a qURL access link with the recipient. Do not extract an identifier from this hostname.

  - **`qurls`**

    `array` — Unordered bounded preview of per-qURL details (maximum 100). Best-effort on single-get and omitted on list, preview lookup failure, or redacted resource types. This preview does not imply newest first or any stable ordering and is not active-first; an active qURL can be absent from the preview when earlier qURLs fill the cap. When present, \`qurl\_count\` is the exact total; if omitted, callers must treat truncation as unknown. For the complete per-resource qURL list, use \`GET /v1/resources/{id}/qurls\`, which pages through every qURL with cursor-based pagination. For large resources, the preview and count are separate reads, so concurrent qURL changes can make the preview length and \`qurl\_count\` briefly differ.

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

      - **`ai_agent_policy`**

        `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

        - **`allow_categories`**

          `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

        - **`block_all`**

          `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

        - **`deny_categories`**

          `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`geo_allowlist`**

        `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`geo_denylist`**

        `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`ip_allowlist`**

        `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`ip_denylist`**

        `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`user_agent_allow_regex`**

        `string` — Regular expression to allow matching user agents (max 256 characters)

      - **`user_agent_deny_regex`**

        `string` — Regular expression to deny matching user agents (max 256 characters)

    - **`created_at`**

      `string`, format: `date-time`

    - **`expires_at`**

      `string`, format: `date-time`

    - **`label`**

      `string`

    - **`max_sessions`**

      `integer`

    - **`one_time_use`**

      `boolean`

    - **`qurl_id`**

      `string`

    - **`qurl_site`**

      `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

    - **`session_duration`**

      `integer` — Session lifetime in seconds (0 = server default)

    - **`status`**

      `string`, possible values: `"active", "consumed", "expired", "revoked"`

    - **`target_path`**

      `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

    - **`use_count`**

      `integer`

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

  - **`slug`**

    `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. See \`ResourceData.slug\` for the full description — the field carries identical semantics on both response schemas.

  - **`status`**

    `string`, possible values: `"active", "revoked"` — Current lifecycle status. Computed at response time — resources past their expires\_at are reported as "expired" even if not explicitly revoked.

  - **`tags`**

    `array` — Tags for categorization and filtering

    **Items:**

    `string`

  - **`target_url`**

    `string`, format: `uri` — The protected backend URL. Optional — omitted (field absent from the JSON object, NOT serialized as null) when the resource is owned by a connector.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "target_url": "",
    "status": "active",
    "created_at": "",
    "expires_at": "",
    "description": "",
    "tags": [
      ""
    ],
    "qurl_site": "",
    "custom_domain": null,
    "slug": "",
    "qurl_count": 1,
    "qurls": [
      {
        "qurl_id": "",
        "label": "",
        "status": "active",
        "one_time_use": true,
        "max_sessions": 1,
        "session_duration": 1,
        "use_count": 1,
        "qurl_site": "",
        "target_path": "",
        "created_at": "",
        "...": "[Additional Properties Truncated]"
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### CreateQurlResponse

- **Type:**`object`

* **`data`**

  `object` — Response data for qURL creation

  - **`branded_domain`**

    `string` — Custom hostname to display as link text. Keep \`qurl\_link\` as the destination. See \`MintLinkData.branded\_domain\` for domain status requirements. An idempotent retry returns the original value. Check the resource and domain again if you need the current domain status.

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`expires_at`**

    `string`, format: `date-time`

  - **`label`**

    `string` — Human-readable label for this qURL

  - **`qurl_id`**

    `string` — Identifier for this qURL. Save it to revoke or update this link, or match its webhook events.

  - **`qurl_link`**

    `string`, format: `uri` — Access link to share with the recipient. Store it securely; later GET requests do not return it. Exact request retries can replay the creation response within the replay window.

  - **`qurl_site`**

    `string`, format: `uri` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

  - **`target_path`**

    `string` — Redirect destination and authorization scope for this new qURL on a qURL Connector resource. Omitted when the new qURL uses the bare origin. Each create call makes an independent qURL; omission affects only that qURL.

  - **`type`**

    `string` — Resource type — echoes the type from the create request.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "qurl_id": "q_3a7f2c8e91b",
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "qurl_link": "",
    "branded_domain": "",
    "qurl_site": "",
    "expires_at": "",
    "target_path": "",
    "label": "",
    "type": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### ResolveQurlRequest

- **Type:**`object`

* **`access_token` (required)**

  `string` — qURL access token (e.g., "at\_k8xqp9h2sj9lx7r4abcdef")

**Example:**

```json
{
  "access_token": "at_k8xqp9h2sj9lx7r4abcdef"
}
```

### ResolveQurlResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`access_grant`**

    `object` — Details of the network access that was granted

    - **`expires_in` (required)**

      `integer` — Seconds until network access expires

    - **`granted_at` (required)**

      `string`, format: `date-time` — When the access was granted

    - **`src_ip` (required)**

      `string` — The IP address that was granted access

  - **`resource_id`**

    `string` — Resource public key. This response does not include a CRID.

  - **`target_url`**

    `string`, format: `uri` — The URL that is now accessible from the caller's IP

* **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "target_url": "https://api.example.com/data",
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "access_grant": {
      "expires_in": 305,
      "granted_at": "2026-03-09T15:30:00Z",
      "src_ip": "203.0.113.42"
    }
  },
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### ResolveQurlData

- **Type:**`object`

* **`access_grant`**

  `object` — Details of the network access that was granted

  - **`expires_in` (required)**

    `integer` — Seconds until network access expires

  - **`granted_at` (required)**

    `string`, format: `date-time` — When the access was granted

  - **`src_ip` (required)**

    `string` — The IP address that was granted access

* **`resource_id`**

  `string` — Resource public key. This response does not include a CRID.

* **`target_url`**

  `string`, format: `uri` — The URL that is now accessible from the caller's IP

**Example:**

```json
{
  "target_url": "https://api.example.com/data",
  "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
  "access_grant": {
    "expires_in": 305,
    "granted_at": "2026-03-09T15:30:00Z",
    "src_ip": "203.0.113.42"
  }
}
```

### AccessGrant

- **Type:**`object`

Details of the network access that was granted

- **`expires_in` (required)**

  `integer` — Seconds until network access expires

- **`granted_at` (required)**

  `string`, format: `date-time` — When the access was granted

- **`src_ip` (required)**

  `string` — The IP address that was granted access

**Example:**

```json
{
  "expires_in": 305,
  "granted_at": "2026-03-09T15:30:00Z",
  "src_ip": "203.0.113.42"
}
```

### QurlListResponse

- **Type:**`object`

* **`data`**

  `array`

  **Items:**

  - **`created_at`**

    `string`, format: `date-time` — When the resource was created.

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

  - **`custom_domain`**

    `string | null` — Custom domain associated with this resource

  - **`description`**

    `string` — Human-readable description

  - **`expires_at`**

    `string`, format: `date-time` — When the resource expires. Each qURL can have its own earlier expiration.

  - **`qurl_count`**

    `integer` — Total number of retained qURLs for this resource, including revoked and expired qURLs that have not yet been removed. This is not an active-link count. If omitted, the count is unknown; do not treat it as zero.

  - **`qurl_site`**

    `string`, format: `uri` — Routing URL for the resource. It does not grant access by itself. Share a qURL access link with the recipient. Do not extract an identifier from this hostname.

  - **`qurls`**

    `array` — Unordered bounded preview of per-qURL details (maximum 100). Best-effort on single-get and omitted on list, preview lookup failure, or redacted resource types. This preview does not imply newest first or any stable ordering and is not active-first; an active qURL can be absent from the preview when earlier qURLs fill the cap. When present, \`qurl\_count\` is the exact total; if omitted, callers must treat truncation as unknown. For the complete per-resource qURL list, use \`GET /v1/resources/{id}/qurls\`, which pages through every qURL with cursor-based pagination. For large resources, the preview and count are separate reads, so concurrent qURL changes can make the preview length and \`qurl\_count\` briefly differ.

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

      - **`ai_agent_policy`**

        `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

        - **`allow_categories`**

          `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

        - **`block_all`**

          `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

        - **`deny_categories`**

          `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

          **Items:**

          `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`geo_allowlist`**

        `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`geo_denylist`**

        `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

        **Items:**

        `string`

      - **`ip_allowlist`**

        `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`ip_denylist`**

        `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

        **Items:**

        `string`

      - **`user_agent_allow_regex`**

        `string` — Regular expression to allow matching user agents (max 256 characters)

      - **`user_agent_deny_regex`**

        `string` — Regular expression to deny matching user agents (max 256 characters)

    - **`created_at`**

      `string`, format: `date-time`

    - **`expires_at`**

      `string`, format: `date-time`

    - **`label`**

      `string`

    - **`max_sessions`**

      `integer`

    - **`one_time_use`**

      `boolean`

    - **`qurl_id`**

      `string`

    - **`qurl_site`**

      `string` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

    - **`session_duration`**

      `integer` — Session lifetime in seconds (0 = server default)

    - **`status`**

      `string`, possible values: `"active", "consumed", "expired", "revoked"`

    - **`target_path`**

      `string` — Redirect destination and authorization scope for this qURL on a qURL Connector resource. The query is returned when one was minted, but authorization checks only the path and its descendants at a segment boundary. Omitted when this qURL uses the bare origin.

    - **`use_count`**

      `integer`

  - **`resource_id`**

    `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

  - **`slug`**

    `string` — Per-owner immutable identity supplied at create time. Echo of the \`slug\` field on \`POST /v1/resources\`; omitted on resources that were not created with a slug. See \`ResourceData.slug\` for the full description — the field carries identical semantics on both response schemas.

  - **`status`**

    `string`, possible values: `"active", "revoked"` — Current lifecycle status. Computed at response time — resources past their expires\_at are reported as "expired" even if not explicitly revoked.

  - **`tags`**

    `array` — Tags for categorization and filtering

    **Items:**

    `string`

  - **`target_url`**

    `string`, format: `uri` — The protected backend URL. Optional — omitted (field absent from the JSON object, NOT serialized as null) when the resource is owned by a connector.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
      "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
      "target_url": "",
      "status": "active",
      "created_at": "",
      "expires_at": "",
      "description": "",
      "tags": [
        ""
      ],
      "qurl_site": "",
      "custom_domain": null,
      "slug": "",
      "qurl_count": 1,
      "qurls": [
        {
          "qurl_id": "",
          "label": "",
          "status": "active",
          "one_time_use": true,
          "max_sessions": 1,
          "session_duration": 1,
          "use_count": 1,
          "qurl_site": "",
          "target_path": "",
          "created_at": "",
          "...": "[Additional Properties Truncated]"
        }
      ]
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### MintLinkData

- **Type:**`object`

* **`branded_domain`**

  `string` — Custom hostname to display as the link text, for example \`customer.com\`. Use \`\<a href="{qurl\_link}">{branded\_domain}\</a>\`; keep \`qurl\_link\` as the destination. Returned only when the domain belongs to your account and has status \`verified\`, \`provisioning\_tls\`, or \`active\`. Otherwise omitted. During \`provisioning\_tls\`, the hostname might not serve HTTPS yet; use it only as display text until \`active\`.

* **`expires_at`**

  `string`, format: `date-time`

* **`qurl_id`**

  `string` — Identifier for this qURL. Save it to manage this link and match \`qurl.accessed\` webhook events.

* **`qurl_link`**

  `string`, format: `uri` — Access link to share with the recipient. Its use limit is set by the request and resource defaults.

* **`target_path`**

  `string` — Redirect destination and authorization scope for this new qURL on a qURL Connector resource. Omitted when the new qURL uses the bare origin. Each mint call makes an independent qURL; omission affects only that qURL.

* **`type`**

  `string` — Resource type — echoes the type of the underlying resource.

**Example:**

```json
{
  "qurl_id": "q_a1b2c3d4e5f",
  "qurl_link": "",
  "branded_domain": "",
  "expires_at": "",
  "target_path": "",
  "type": ""
}
```

### MintLinkResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`branded_domain`**

    `string` — Custom hostname to display as the link text, for example \`customer.com\`. Use \`\<a href="{qurl\_link}">{branded\_domain}\</a>\`; keep \`qurl\_link\` as the destination. Returned only when the domain belongs to your account and has status \`verified\`, \`provisioning\_tls\`, or \`active\`. Otherwise omitted. During \`provisioning\_tls\`, the hostname might not serve HTTPS yet; use it only as display text until \`active\`.

  - **`expires_at`**

    `string`, format: `date-time`

  - **`qurl_id`**

    `string` — Identifier for this qURL. Save it to manage this link and match \`qurl.accessed\` webhook events.

  - **`qurl_link`**

    `string`, format: `uri` — Access link to share with the recipient. Its use limit is set by the request and resource defaults.

  - **`target_path`**

    `string` — Redirect destination and authorization scope for this new qURL on a qURL Connector resource. Omitted when the new qURL uses the bare origin. Each mint call makes an independent qURL; omission affects only that qURL.

  - **`type`**

    `string` — Resource type — echoes the type of the underlying resource.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "qurl_id": "q_a1b2c3d4e5f",
    "qurl_link": "",
    "branded_domain": "",
    "expires_at": "",
    "target_path": "",
    "type": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### ShareResourceRequest

- **Type:**`object`

Request body for `POST /v1/resources/{id}/share`. Every field is optional — send `{}` to accept the default share-link lifetime.

- **`session_duration`**

  `string` — Lifetime of each admitted session, independent of the link lifetime. Uses the same duration syntax and 1-second to 24-hour bounds as mint\_link. The resource's session-duration cap still applies; exceeding it returns \`400 invalid\_session\_duration\`. Omit this field to preserve the resource or service default. The session starts on first authorized content access; initial network admission includes the existing 10-second arrival allowance.

- **`ttl_seconds`**

  `integer` — Requested link lifetime in whole seconds. Omit it to use the service default. Values from 1 to 59 are raised to 60 seconds; values above 2,592,000 are lowered to 30 days. Zero, negative values, and non-integers fail request validation. The resource's expiration can shorten the result further. Read \`expires\_at\` and \`expires\_in\_seconds\` in the response for the effective lifetime.

**Example:**

```json
{
  "ttl_seconds": 300,
  "session_duration": "5m"
}
```

### ShareResourceData

- **Type:**`object`

* **`expires_at` (required)**

  `string`, format: `date-time` — When the share link stops working — the effective, post-clamp expiry.

* **`expires_in_seconds` (required)**

  `integer` — Seconds until \`expires\_at\`, measured when the response was produced. This is the effective lifetime after clamping, so it may be lower than the requested \`ttl\_seconds\`.

* **`publisher` (required)**

  `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

  - **`verified` (required)**

    `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

  - **`name`**

    `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

* **`qurl` (required)**

  `string`, format: `uri` — The freshly minted share link — a short-lived qURL access link. Returned once and never retrievable again; whoever holds the link can use it, so treat it as a secret and hand it only to the intended recipient.

* **`qurl_id` (required)**

  `string` — Identifier for the new qURL. Save it to revoke this link with \`DELETE /v1/resources/{id}/qurls/{qurl\_id}\` using the resource CRID, and to match access events.

* **`resource_created_at` (required)**

  `string`, format: `date-time` — When the shared resource was created. This is not when this link was minted: the link is new, and \`expires\_at\` is the link's expiry.

* **`single_use` (required)**

  `boolean` — Whether the share link is consumed by its first use. Set from the resource type's minting defaults (qURL Connector links default to single-visitor use).

* **`type` (required)**

  `string` — The shared resource's type (see \`ResourceType\` for the full enum).

* **`crid`**

  `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

**Example:**

```json
{
  "qurl_id": "q_a1b2c3d4e5f",
  "qurl": "https://qurl.link/#qv2t1.1.1.1.Y2xhaW1zLWV4YW1wbGU.c2VjcmV0LWV4YW1wbGU.c2lnbmF0dXJlLWV4YW1wbGU",
  "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
  "type": "url",
  "expires_at": "2026-03-09T15:35:00Z",
  "expires_in_seconds": 300,
  "single_use": false,
  "resource_created_at": "2026-03-01T09:00:00Z",
  "publisher": {
    "name": "Acme Docs",
    "verified": false
  }
}
```

### ShareResourceResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`expires_at` (required)**

    `string`, format: `date-time` — When the share link stops working — the effective, post-clamp expiry.

  - **`expires_in_seconds` (required)**

    `integer` — Seconds until \`expires\_at\`, measured when the response was produced. This is the effective lifetime after clamping, so it may be lower than the requested \`ttl\_seconds\`.

  - **`publisher` (required)**

    `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

    - **`verified` (required)**

      `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

    - **`name`**

      `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

  - **`qurl` (required)**

    `string`, format: `uri` — The freshly minted share link — a short-lived qURL access link. Returned once and never retrievable again; whoever holds the link can use it, so treat it as a secret and hand it only to the intended recipient.

  - **`qurl_id` (required)**

    `string` — Identifier for the new qURL. Save it to revoke this link with \`DELETE /v1/resources/{id}/qurls/{qurl\_id}\` using the resource CRID, and to match access events.

  - **`resource_created_at` (required)**

    `string`, format: `date-time` — When the shared resource was created. This is not when this link was minted: the link is new, and \`expires\_at\` is the link's expiry.

  - **`single_use` (required)**

    `boolean` — Whether the share link is consumed by its first use. Set from the resource type's minting defaults (qURL Connector links default to single-visitor use).

  - **`type` (required)**

    `string` — The shared resource's type (see \`ResourceType\` for the full enum).

  - **`crid`**

    `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "qurl_id": "q_a1b2c3d4e5f",
    "qurl": "https://qurl.link/#qv2t1.1.1.1.Y2xhaW1zLWV4YW1wbGU.c2VjcmV0LWV4YW1wbGU.c2lnbmF0dXJlLWV4YW1wbGU",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "type": "url",
    "expires_at": "2026-03-09T15:35:00Z",
    "expires_in_seconds": 300,
    "single_use": false,
    "resource_created_at": "2026-03-01T09:00:00Z",
    "publisher": {
      "name": "Acme Docs",
      "verified": false
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### CreateDelegatedQurlBatchRequest

- **Type:**`object`

* **`grants` (required)**

  `array` — Independent recipient grants, processed in input order

  **Items:**

  - **`expires_in`**

    `string` — qURL lifetime. The delegated capability sets the maximum. If omitted, the service uses the capability maximum.

  - **`label`**

    `string` — Human-readable recipient label, at most 500 UTF-8 bytes

  - **`max_sessions`**

    `integer` — Maximum sessions for this qURL; zero means unlimited

  - **`one_time_use`**

    `boolean`, default: `false` — Whether the qURL is consumed by its first admission

  - **`session_duration`**

    `string` — Access lifetime after a successful admission

* **`mint_capability` (required)**

  `string` — Opaque delegated mint capability returned by the upload service

**Example:**

```json
{
  "mint_capability": "",
  "grants": [
    {
      "expires_in": "1h",
      "label": "",
      "one_time_use": false,
      "max_sessions": 0,
      "session_duration": "1h"
    }
  ]
}
```

### DelegatedQurlGrant

- **Type:**`object`

* **`expires_in`**

  `string` — qURL lifetime. The delegated capability sets the maximum. If omitted, the service uses the capability maximum.

* **`label`**

  `string` — Human-readable recipient label, at most 500 UTF-8 bytes

* **`max_sessions`**

  `integer` — Maximum sessions for this qURL; zero means unlimited

* **`one_time_use`**

  `boolean`, default: `false` — Whether the qURL is consumed by its first admission

* **`session_duration`**

  `string` — Access lifetime after a successful admission

**Example:**

```json
{
  "expires_in": "1h",
  "label": "",
  "one_time_use": false,
  "max_sessions": 0,
  "session_duration": "1h"
}
```

### DelegatedQurlBatchStatus

- **Type:**`string`

**Example:**

### DelegatedQurlBatchAcceptedData

- **Type:**`object`

* **`batch_id` (required)**

  `string`

* **`item_count` (required)**

  `integer`

* **`status` (required)**

  `string`, possible values: `"queued"`

* **`submitted_at` (required)**

  `string`, format: `date-time`

**Example:**

```json
{
  "batch_id": "",
  "status": "queued",
  "item_count": 1,
  "submitted_at": ""
}
```

### CreateDelegatedQurlBatchResponse

- **Type:**`object`

* **`data` (required)**

  `object`

  - **`batch_id` (required)**

    `string`

  - **`item_count` (required)**

    `integer`

  - **`status` (required)**

    `string`, possible values: `"queued"`

  - **`submitted_at` (required)**

    `string`, format: `date-time`

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "batch_id": "",
    "status": "queued",
    "item_count": 1,
    "submitted_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### DelegatedQurlBatchData

- **Type:**`object`

* **`batch_id` (required)**

  `string`

* **`item_count` (required)**

  `integer`

* **`status` (required)**

  `string`, possible values: `"queued", "running", "succeeded", "partially_failed", "failed"`

* **`submitted_at` (required)**

  `string`, format: `date-time`

* **`completed_at`**

  `string`, format: `date-time`

* **`results`**

  `array` — Results in the same order as the submitted grants

  **Items:**

  - **`index` (required)**

    `integer`

  - **`status` (required)**

    `string`, possible values: `"succeeded", "failed"`

  - **`error`**

    `object`

    - **`code` (required)**

      `string`, possible values: `"creation_failed"`

    - **`message` (required)**

      `string`

  - **`qurl`**

    `object`

    - **`expires_at` (required)**

      `string`, format: `date-time`

    - **`qurl_id` (required)**

      `string`

    - **`qurl_link` (required)**

      `string`, format: `uri` — Fresh share link returned only in the terminal batch result

**Example:**

```json
{
  "batch_id": "",
  "status": "queued",
  "item_count": 1,
  "submitted_at": "",
  "completed_at": "",
  "results": [
    {
      "index": 0,
      "status": "succeeded",
      "qurl": {
        "qurl_id": "",
        "qurl_link": "",
        "expires_at": ""
      },
      "error": {
        "code": "creation_failed",
        "message": ""
      }
    }
  ]
}
```

### GetDelegatedQurlBatchResponse

- **Type:**`object`

* **`data` (required)**

  `object`

  - **`batch_id` (required)**

    `string`

  - **`item_count` (required)**

    `integer`

  - **`status` (required)**

    `string`, possible values: `"queued", "running", "succeeded", "partially_failed", "failed"`

  - **`submitted_at` (required)**

    `string`, format: `date-time`

  - **`completed_at`**

    `string`, format: `date-time`

  - **`results`**

    `array` — Results in the same order as the submitted grants

    **Items:**

    - **`index` (required)**

      `integer`

    - **`status` (required)**

      `string`, possible values: `"succeeded", "failed"`

    - **`error`**

      `object`

      - **`code` (required)**

        `string`, possible values: `"creation_failed"`

      - **`message` (required)**

        `string`

    - **`qurl`**

      `object`

      - **`expires_at` (required)**

        `string`, format: `date-time`

      - **`qurl_id` (required)**

        `string`

      - **`qurl_link` (required)**

        `string`, format: `uri` — Fresh share link returned only in the terminal batch result

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "batch_id": "",
    "status": "queued",
    "item_count": 1,
    "submitted_at": "",
    "completed_at": "",
    "results": [
      {
        "index": 0,
        "status": "succeeded",
        "qurl": {
          "qurl_id": "",
          "qurl_link": "",
          "expires_at": ""
        },
        "error": {
          "code": "creation_failed",
          "message": ""
        }
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### DelegatedQurlBatchItemResult

- **Type:**`object`

* **`index` (required)**

  `integer`

* **`status` (required)**

  `string`, possible values: `"succeeded", "failed"`

* **`error`**

  `object`

  - **`code` (required)**

    `string`, possible values: `"creation_failed"`

  - **`message` (required)**

    `string`

* **`qurl`**

  `object`

  - **`expires_at` (required)**

    `string`, format: `date-time`

  - **`qurl_id` (required)**

    `string`

  - **`qurl_link` (required)**

    `string`, format: `uri` — Fresh share link returned only in the terminal batch result

**Example:**

```json
{
  "index": 0,
  "status": "succeeded",
  "qurl": {
    "qurl_id": "",
    "qurl_link": "",
    "expires_at": ""
  },
  "error": {
    "code": "creation_failed",
    "message": ""
  }
}
```

### DelegatedQurlCreatedData

- **Type:**`object`

* **`expires_at` (required)**

  `string`, format: `date-time`

* **`qurl_id` (required)**

  `string`

* **`qurl_link` (required)**

  `string`, format: `uri` — Fresh share link returned only in the terminal batch result

**Example:**

```json
{
  "qurl_id": "",
  "qurl_link": "",
  "expires_at": ""
}
```

### DelegatedQurlItemError

- **Type:**`object`

* **`code` (required)**

  `string`, possible values: `"creation_failed"`

* **`message` (required)**

  `string`

**Example:**

```json
{
  "code": "creation_failed",
  "message": ""
}
```

### DelegatedQurlData

- **Type:**`object`

* **`expires_at` (required)**

  `string`, format: `date-time`

* **`max_sessions` (required)**

  `integer`

* **`one_time_use` (required)**

  `boolean`

* **`qurl_id` (required)**

  `string`

* **`session_duration` (required)**

  `integer` — Session lifetime in seconds

* **`status` (required)**

  `string`, possible values: `"active", "consumed", "expired", "revoked"`

* **`label`**

  `string`

**Example:**

```json
{
  "qurl_id": "",
  "status": "active",
  "expires_at": "",
  "label": "",
  "one_time_use": true,
  "max_sessions": 0,
  "session_duration": 0
}
```

### GetDelegatedQurlResponse

- **Type:**`object`

* **`data` (required)**

  `object`

  - **`expires_at` (required)**

    `string`, format: `date-time`

  - **`max_sessions` (required)**

    `integer`

  - **`one_time_use` (required)**

    `boolean`

  - **`qurl_id` (required)**

    `string`

  - **`session_duration` (required)**

    `integer` — Session lifetime in seconds

  - **`status` (required)**

    `string`, possible values: `"active", "consumed", "expired", "revoked"`

  - **`label`**

    `string`

* **`meta` (required)**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "qurl_id": "",
    "status": "active",
    "expires_at": "",
    "label": "",
    "one_time_use": true,
    "max_sessions": 0,
    "session_duration": 0
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### BatchCreateRequest

- **Type:**`object`

* **`items` (required)**

  `array` — Array of qURL creation requests (1-100 items)

  **Items:**

  - **`access_policy`**

    `object` — Access control policy for the qURL. Signed qURL links do not evaluate this policy; a policy stored on one has no effect on who can open it.

    - **`ai_agent_policy`**

      `object` — Control access by recognized AI agent category instead of writing User-Agent patterns. This checks the User-Agent header; agents that hide or change their identity can bypass it. IP and country rules are checked first. This policy is checked next, followed by \`user\_agent\_deny\_regex\` and \`user\_agent\_allow\_regex\`. Deny rules take priority: an agent allowed here can still be blocked by a User-Agent deny rule. A User-Agent allow rule cannot override a denial here. Block all recognized AI agents: \`\`\`json {"block\_all": true} \`\`\` Block specific crawlers: \`\`\`json {"deny\_categories": \["gptbot", "commoncrawl"]} \`\`\` Allow only Claude and Perplexity among recognized AI agents: \`\`\`json {"allow\_categories": \["claude", "perplexity"]} \`\`\` If a category appears in both lists, access is denied. These category rules do not identify or authenticate the person using an agent.

      - **`allow_categories`**

        `array` — AI agent categories to permit. When set, all other AI agents are blocked. Deny takes precedence. Ignored if block\_all is true.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

      - **`block_all`**

        `boolean`, default: `false` — Block all recognized AI agents regardless of category lists

      - **`deny_categories`**

        `array` — AI agent categories to block. Ignored if block\_all is true. Deny takes precedence over allow.

        **Items:**

        `string`, possible values: `"chatgpt", "gptbot", "claude", "gemini", "perplexity", "cohere", "meta", "bytedance", "amazon", "apple", "commoncrawl", "mistral", "generic_ai"` — A well-known AI agent category identifier

    - **`geo_allowlist`**

      `array` — List of allowed country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`geo_denylist`**

      `array` — List of denied country codes (ISO 3166-1 alpha-2, max 50 entries)

      **Items:**

      `string`

    - **`ip_allowlist`**

      `array` — List of allowed IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`ip_denylist`**

      `array` — List of denied IP addresses or CIDR ranges (max 100 entries)

      **Items:**

      `string`

    - **`user_agent_allow_regex`**

      `string` — Regular expression to allow matching user agents (max 256 characters)

    - **`user_agent_deny_regex`**

      `string` — Regular expression to deny matching user agents (max 256 characters)

  - **`custom_domain`**

    `string` — Optional custom domain to assign to the auto-created resource. The domain must be registered, active, and owned by the caller. Only allowed when the target URL creates a new resource — rejected if a resource already exists for this target URL.

  - **`expires_in`**

    `string` — How long the new link remains valid. Examples: \`5m\`, \`1h\`, \`7d\`, \`1w\`. Minimum: 1 minute. Maximum: 30 days on all plans. The resource's expiration can shorten the link's lifetime; read \`expires\_at\` in the response. When omitted or empty, qURL Connector links use 5 minutes. URL and integration-owned upload links use the service default. Set this field explicitly for a predictable lifetime.

  - **`label`**

    `string` — Human-readable label identifying who this qURL is for

  - **`max_sessions`**

    `integer` — Maximum concurrent sessions for this qURL, not the whole resource. Use \`0\` for no limit or \`1\`–\`1000\` for a limit. Set \`one\_time\_use: false\` explicitly when you need multiple visitors: some resource types otherwise create a single-visitor link even when \`max\_sessions\` is \`0\`. Visitors that share a source IP count as one session and share its IP policy checks.

  - **`one_time_use`**

    `boolean` — Whether the link is consumed by its first successful use. Explicit \`true\` or \`false\` takes precedence over the resource defaults. When omitted, URL and integration-owned upload links use \`false\`. qURL Connector links use \`true\` unless \`max\_sessions\` is greater than 1. For a reusable Connector link, explicitly send \`one\_time\_use: false\`, including when \`max\_sessions\` is \`0\` (unlimited).

  - **`private`**

    `boolean` — Privacy of the resource, used only when this request creates it. When omitted, a new resource is private. Send \`false\` to create a public resource. Privacy cannot change after creation. When the request reuses your existing resource for the same \`target\_url\`, omit this field: the resource keeps the privacy it has. A value that differs from the existing resource returns \`400 privacy\_mismatch\`.

  - **`session_duration`**

    `string` — How long a session permits new requests after the recipient first reaches the content. This is separate from \`expires\_in\`, which controls the link's expiration. \*\*Default and limits:\*\* Supports seconds (\`30s\`), minutes (\`5m\`), hours (\`1h\`), and days (\`1d\`). Minimum: 1 second. Maximum: 24 hours. If omitted, uses the resource cap when set. Without a cap, qURL Connectors use 1 hour; other resource types use the service default. Set this field explicitly when timing matters. \*\*Resource cap:\*\* On the first create for a target URL, an explicit value also sets the resource's fixed \`session\_duration\_cap\`. Later creates, mints, and updates cannot exceed it (\`400 invalid\_session\_duration\`). See \`ResourceData.session\_duration\_cap\` for how to create a resource with a different cap. \*\*Timing precision:\*\* For signed browser qURLs with a configured duration, the first content request must be authorized within 10 seconds of the start of admission, including time spent completing admission. The session timer then runs for \`session\_duration\` from that first authorization. Initial network admission lasts \`session\_duration + 10 seconds\`; it does not double for long sessions. A 24-hour session gets 24 hours plus 10 seconds of initial network admission. Opening the original invitation again is not required during a live session, including after a TCP connection becomes idle. When duration is omitted on an uncapped URL resource, the existing default authorization deadline is 24 hours plus 10 seconds from admission; the configured-duration first-arrival rule does not apply. New requests are denied at session expiry. Active requests, streams, and WebSockets are canceled at that deadline. The network allowance does not extend application authorization. A single-use invitation remains consumed if the first content request misses the 10-second arrival deadline. Cancellation cannot retract data already delivered or buffered by the client.

  - **`target_url`**

    `string`, format: `uri` — The URL to protect with qURL (max 2048 characters). Required for standard reverse-proxy qURLs.

  - **`type`**

    `string`, default: `"url"` — Type of resource the qURL grants access to. Defaults to \`url\`; other values are restricted to specific integrations. See \`ResourceType\`.

**Example:**

```json
{
  "items": [
    {
      "type": "url",
      "private": true,
      "target_url": "https://dashboard.example.com",
      "expires_in": "1h",
      "one_time_use": true,
      "max_sessions": 0,
      "session_duration": "1h",
      "access_policy": {
        "ip_allowlist": [
          "192.168.1.0/24",
          "10.0.0.1"
        ],
        "ip_denylist": [
          ""
        ],
        "geo_allowlist": [
          "US",
          "CA",
          "GB"
        ],
        "geo_denylist": [
          "CN",
          "RU"
        ],
        "user_agent_allow_regex": "^Mozilla.*",
        "user_agent_deny_regex": ".*bot.*",
        "ai_agent_policy": {
          "block_all": false,
          "deny_categories": [
            "gptbot",
            "commoncrawl",
            "bytedance"
          ],
          "allow_categories": [
            "claude",
            "chatgpt",
            "perplexity"
          ]
        }
      },
      "label": "Alice from Acme",
      "custom_domain": "app.example.com"
    }
  ]
}
```

### BatchCreateResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`failed`**

    `integer` — Number of failed creations

  - **`results`**

    `array`

    **Items:**

    - **`branded_domain`**

      `string` — Customer's bare branded hostname (if success). See \`MintLinkData.branded\_domain\` for the canonical description, including the \`\<a href="{qurl\_link}">{branded\_domain}\</a>\` rendering pattern, the \`provisioning\_tls\` HTTPS-not-yet-live caveat, and the live-status + tenant-ownership gating.

    - **`crid`**

      `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

    - **`error`**

      `object` — Error details (if failed)

      - **`code`**

        `string`

      - **`message`**

        `string`

    - **`expires_at`**

      `string`, format: `date-time` — Expiration time (if success)

    - **`index`**

      `integer` — Original index in the request array

    - **`qurl_link`**

      `string`, format: `uri` — Access link (if success)

    - **`qurl_site`**

      `string`, format: `uri` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

    - **`resource_id`**

      `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

    - **`success`**

      `boolean`

  - **`succeeded`**

    `integer` — Number of successfully created qURLs

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "succeeded": 1,
    "failed": 1,
    "results": [
      {
        "index": 1,
        "success": true,
        "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
        "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
        "qurl_link": "",
        "branded_domain": "",
        "qurl_site": "",
        "expires_at": "",
        "error": {
          "code": "",
          "message": ""
        }
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### BatchItemResult

- **Type:**`object`

* **`branded_domain`**

  `string` — Customer's bare branded hostname (if success). See \`MintLinkData.branded\_domain\` for the canonical description, including the \`\<a href="{qurl\_link}">{branded\_domain}\</a>\` rendering pattern, the \`provisioning\_tls\` HTTPS-not-yet-live caveat, and the live-status + tenant-ownership gating.

* **`crid`**

  `string` — The preferred identifier for a resource. Use the \`crid\` value in resource paths and wherever a request asks for a resource identifier. It remains valid for the resource's lifetime, does not depend on its location, and is never reused after deletion. A resource can retain compatible CRID aliases when its canonical identifier is corrected. A resource path addressed by CRID returns that CRID. Paths addressed by resource public key and resource lists return the canonical CRID. A CRID alone grants no access. This API creates an access link for the resource's owner or, for a private resource, for a device the publisher allowed. Older resources can omit this field. Use their \`resource\_id\` public key as a fallback.

* **`error`**

  `object` — Error details (if failed)

  - **`code`**

    `string`

  - **`message`**

    `string`

* **`expires_at`**

  `string`, format: `date-time` — Expiration time (if success)

* **`index`**

  `integer` — Original index in the request array

* **`qurl_link`**

  `string`, format: `uri` — Access link (if success)

* **`qurl_site`**

  `string`, format: `uri` — Routing URL for this qURL. Use it as returned. Do not extract an identifier from its hostname.

* **`resource_id`**

  `string` — Resource public key, retained for compatibility. Prefer \`crid\` when addressing this resource.

* **`success`**

  `boolean`

**Example:**

```json
{
  "index": 1,
  "success": true,
  "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
  "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
  "qurl_link": "",
  "branded_domain": "",
  "qurl_site": "",
  "expires_at": "",
  "error": {
    "code": "",
    "message": ""
  }
}
```

### QuotaData

- **Type:**`object`

* **`period_end`**

  `string`, format: `date-time`

* **`period_start`**

  `string`, format: `date-time`

* **`plan`**

  `string`, possible values: `"free", "growth", "enterprise"`

* **`rate_limits`**

  `object`

  - **`create_per_hour`**

    `integer`

  - **`create_per_minute`**

    `integer`

  - **`list_per_minute`**

    `integer`

  - **`max_active_qurls`**

    `integer` — Deprecated — use \`max\_active\_resources\`, whose value this mirrors. Despite the name it has always reported the protected-resource cap, never a qURL cap. For the qURL cap, read \`max\_qurls\`.

  - **`max_active_resources`**

    `integer` — Maximum active protected resources — the target URLs and qURL Connectors you protect (-1 = unlimited). Reached only when protecting something new; creating more qURLs for an existing resource does not use this allowance. Other limits, access rules, and resource status still apply.

  - **`max_data_transfer_bytes`**

    `integer`, format: `int64` — Maximum data proxied through your protected resources per calendar month, in bytes (-1 = unlimited).

  - **`max_expiry_seconds`**

    `integer` — Maximum expiry duration in seconds (2592000, or 30 days, on all customer plans)

  - **`max_qurls`**

    `integer` — Maximum qURLs you may hold across all your protected resources (-1 = unlimited). Currently unlimited on every plan — read the live value rather than assuming it. \`max\_active\_resources\` bounds how many things you may protect. It does not limit how many qURLs you may issue for one protected resource.

  - **`max_tokens_per_qurl`**

    `integer` — Deprecated compatibility field. Always -1 (unlimited); there is no per-resource qURL cap.

  - **`resolve_per_minute`**

    `integer` — Sustained token resolution refill rate per minute (-1 = unlimited). The instantaneous burst allowance can be smaller; X-RateLimit-Limit reports that burst allowance.

* **`usage`**

  `object`

  - **`active_qurls`**

    `integer` — Deprecated — use \`active\_resources\`, whose value this mirrors. Despite the name it has always reported the count of active protected resources.

  - **`active_qurls_percent`**

    `number | null`, format: `float` — Deprecated — use \`active\_resources\_percent\`, whose value this mirrors, including the fact that it can exceed 100 and that null means either "unlimited" or "plan could not be determined".

  - **`active_resources`**

    `integer` — Currently active protected resources

  - **`active_resources_percent`**

    `number | null`, format: `float` — Percentage of max\_active\_resources used. Null in TWO cases, which a client must not conflate: your plan is unlimited, or your plan could not be determined right now — in the second case the limits reported alongside it are placeholders, so do not render "unlimited" on a null alone. Can exceed 100: the cap is applied when you protect something new, so an account already above it keeps everything it has and reports over 100 until it drops back under.

  - **`data_transfer_available`**

    `boolean` — Whether traffic measurement is enabled and the account usage was resolved.

  - **`data_transfer_bytes`**

    `integer`, format: `int64` — Request and response bytes proxied in the current UTC calendar month. Recent traffic can take a short time to appear. Use only when data\_transfer\_available is true.

  - **`qurls_created`**

    `integer` — Total recorded qURL creations and link mints this UTC calendar month, including share links. Counts update asynchronously.

  - **`total_accesses`**

    `integer` — Total access attempts this period

**Example:**

```json
{
  "plan": "free",
  "period_start": "",
  "period_end": "",
  "rate_limits": {
    "create_per_minute": 1,
    "create_per_hour": 1,
    "list_per_minute": 1,
    "resolve_per_minute": 1,
    "max_qurls": 1,
    "max_active_resources": 1,
    "max_data_transfer_bytes": 1,
    "max_expiry_seconds": 1
  },
  "usage": {
    "qurls_created": 1,
    "active_resources": 1,
    "active_resources_percent": null,
    "data_transfer_bytes": 1,
    "data_transfer_available": true,
    "total_accesses": 1
  }
}
```

### QuotaResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`period_end`**

    `string`, format: `date-time`

  - **`period_start`**

    `string`, format: `date-time`

  - **`plan`**

    `string`, possible values: `"free", "growth", "enterprise"`

  - **`rate_limits`**

    `object`

    - **`create_per_hour`**

      `integer`

    - **`create_per_minute`**

      `integer`

    - **`list_per_minute`**

      `integer`

    - **`max_active_qurls`**

      `integer` — Deprecated — use \`max\_active\_resources\`, whose value this mirrors. Despite the name it has always reported the protected-resource cap, never a qURL cap. For the qURL cap, read \`max\_qurls\`.

    - **`max_active_resources`**

      `integer` — Maximum active protected resources — the target URLs and qURL Connectors you protect (-1 = unlimited). Reached only when protecting something new; creating more qURLs for an existing resource does not use this allowance. Other limits, access rules, and resource status still apply.

    - **`max_data_transfer_bytes`**

      `integer`, format: `int64` — Maximum data proxied through your protected resources per calendar month, in bytes (-1 = unlimited).

    - **`max_expiry_seconds`**

      `integer` — Maximum expiry duration in seconds (2592000, or 30 days, on all customer plans)

    - **`max_qurls`**

      `integer` — Maximum qURLs you may hold across all your protected resources (-1 = unlimited). Currently unlimited on every plan — read the live value rather than assuming it. \`max\_active\_resources\` bounds how many things you may protect. It does not limit how many qURLs you may issue for one protected resource.

    - **`max_tokens_per_qurl`**

      `integer` — Deprecated compatibility field. Always -1 (unlimited); there is no per-resource qURL cap.

    - **`resolve_per_minute`**

      `integer` — Sustained token resolution refill rate per minute (-1 = unlimited). The instantaneous burst allowance can be smaller; X-RateLimit-Limit reports that burst allowance.

  - **`usage`**

    `object`

    - **`active_qurls`**

      `integer` — Deprecated — use \`active\_resources\`, whose value this mirrors. Despite the name it has always reported the count of active protected resources.

    - **`active_qurls_percent`**

      `number | null`, format: `float` — Deprecated — use \`active\_resources\_percent\`, whose value this mirrors, including the fact that it can exceed 100 and that null means either "unlimited" or "plan could not be determined".

    - **`active_resources`**

      `integer` — Currently active protected resources

    - **`active_resources_percent`**

      `number | null`, format: `float` — Percentage of max\_active\_resources used. Null in TWO cases, which a client must not conflate: your plan is unlimited, or your plan could not be determined right now — in the second case the limits reported alongside it are placeholders, so do not render "unlimited" on a null alone. Can exceed 100: the cap is applied when you protect something new, so an account already above it keeps everything it has and reports over 100 until it drops back under.

    - **`data_transfer_available`**

      `boolean` — Whether traffic measurement is enabled and the account usage was resolved.

    - **`data_transfer_bytes`**

      `integer`, format: `int64` — Request and response bytes proxied in the current UTC calendar month. Recent traffic can take a short time to appear. Use only when data\_transfer\_available is true.

    - **`qurls_created`**

      `integer` — Total recorded qURL creations and link mints this UTC calendar month, including share links. Counts update asynchronously.

    - **`total_accesses`**

      `integer` — Total access attempts this period

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "plan": "free",
    "period_start": "",
    "period_end": "",
    "rate_limits": {
      "create_per_minute": 1,
      "create_per_hour": 1,
      "list_per_minute": 1,
      "resolve_per_minute": 1,
      "max_qurls": 1,
      "max_active_resources": 1,
      "max_data_transfer_bytes": 1,
      "max_expiry_seconds": 1
    },
    "usage": {
      "qurls_created": 1,
      "active_resources": 1,
      "active_resources_percent": null,
      "data_transfer_bytes": 1,
      "data_transfer_available": true,
      "total_accesses": 1
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### MeData

- **Type:**`object`

* **`auth_type` (required)**

  `string` — How this request authenticated: a qURL API key (\`api\_key\`) or an OAuth 2.1 JWT (\`jwt\`).

* **`owner_id` (required)**

  `string` — Identifier of the account the credential belongs to.

* **`api_key`**

  `object` — Identity of the API key that authenticated this request. Present only when \`auth\_type\` is \`api\_key\`; JWT-authenticated requests omit it.

  - **`key_id` (required)**

    `string`

  - **`kind` (required)**

    `string` — The credential kind: a durable \`api\_key\`, a one-shot \`enrollment\_token\`, or a system-minted per-device \`device\` credential. Unknown future kind values may appear as the credential model grows; clients must tolerate values beyond these.

  - **`scopes` (required)**

    `array` — Permissions granted to this credential. Returned in alphabetical order for deterministic responses.

    **Items:**

    `string`

  - **`expires_at`**

    `string`, format: `date-time` — Expiration timestamp. Omitted for non-expiring credentials.

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

**Example:**

```json
{
  "owner_id": "auth0|user123",
  "auth_type": "api_key",
  "api_key": {
    "key_id": "",
    "key_prefix": "",
    "kind": "api_key",
    "scopes": [
      ""
    ],
    "expires_at": ""
  }
}
```

### MeApiKey

- **Type:**`object`

Identity of the API key that authenticated this request. Present only when `auth_type` is `api_key`; JWT-authenticated requests omit it.

- **`key_id` (required)**

  `string`

- **`kind` (required)**

  `string` — The credential kind: a durable \`api\_key\`, a one-shot \`enrollment\_token\`, or a system-minted per-device \`device\` credential. Unknown future kind values may appear as the credential model grows; clients must tolerate values beyond these.

- **`scopes` (required)**

  `array` — Permissions granted to this credential. Returned in alphabetical order for deterministic responses.

  **Items:**

  `string`

- **`expires_at`**

  `string`, format: `date-time` — Expiration timestamp. Omitted for non-expiring credentials.

- **`key_prefix`**

  `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

**Example:**

```json
{
  "key_id": "",
  "key_prefix": "",
  "kind": "api_key",
  "scopes": [
    ""
  ],
  "expires_at": ""
}
```

### MeResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`auth_type` (required)**

    `string` — How this request authenticated: a qURL API key (\`api\_key\`) or an OAuth 2.1 JWT (\`jwt\`).

  - **`owner_id` (required)**

    `string` — Identifier of the account the credential belongs to.

  - **`api_key`**

    `object` — Identity of the API key that authenticated this request. Present only when \`auth\_type\` is \`api\_key\`; JWT-authenticated requests omit it.

    - **`key_id` (required)**

      `string`

    - **`kind` (required)**

      `string` — The credential kind: a durable \`api\_key\`, a one-shot \`enrollment\_token\`, or a system-minted per-device \`device\` credential. Unknown future kind values may appear as the credential model grows; clients must tolerate values beyond these.

    - **`scopes` (required)**

      `array` — Permissions granted to this credential. Returned in alphabetical order for deterministic responses.

      **Items:**

      `string`

    - **`expires_at`**

      `string`, format: `date-time` — Expiration timestamp. Omitted for non-expiring credentials.

    - **`key_prefix`**

      `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "owner_id": "auth0|user123",
    "auth_type": "api_key",
    "api_key": {
      "key_id": "",
      "key_prefix": "",
      "kind": "api_key",
      "scopes": [
        ""
      ],
      "expires_at": ""
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### UsageCostEstimate

- **Type:**`object`

* **`amount_cents` (required)**

  `integer`, format: `int64` — Cost in cents

* **`currency` (required)**

  `string` — Currency code (e.g. "usd")

* **`description` (required)**

  `string` — Human-readable breakdown

**Example:**

```json
{
  "currency": "usd",
  "amount_cents": 1500,
  "description": "150 qURLs x $0.10/qURL"
}
```

### UsageCurrentPeriodData

- **Type:**`object`

* **`active_qurls` (required)**

  `integer` — Deprecated — use \`active\_resources\`, whose value this mirrors. Despite the name it has always reported the count of active protected resources.

* **`active_resources` (required)**

  `integer` — Number of currently active protected resources — the target URLs and qURL Connectors you protect.

* **`data_transfer_bytes` (required)**

  `integer`, format: `int64` — Data proxied through your protected resources in the current UTC calendar month, in bytes. This is independent of the stored Stripe billing period. Reflects traffic measured by the metering pipeline; reads \`0\` where measurement is not yet enabled, and can lag the most recent traffic by a short interval.

* **`period_end` (required)**

  `string`, format: `date-time` — Exclusive end of the stored Stripe billing period, or exclusive end of the current UTC calendar month when no Stripe period is stored

* **`period_start` (required)**

  `string`, format: `date-time` — Start of the stored Stripe billing period, or the current UTC calendar month when no Stripe period is stored

* **`qurls_created` (required)**

  `integer` — Committed metered usage in the stored Stripe billing period, or recorded qURL creations and link mints in the current UTC calendar month when no Stripe period is stored

* **`tier` (required)**

  `string`, possible values: `"free", "growth", "enterprise"` — Current billing tier

* **`unit_price_cents` (required)**

  `integer`, format: `int64` — Stored Stripe unit price in cents; zero when no metered price is stored

* **`cost_estimate`**

  `object`

  - **`amount_cents` (required)**

    `integer`, format: `int64` — Cost in cents

  - **`currency` (required)**

    `string` — Currency code (e.g. "usd")

  - **`description` (required)**

    `string` — Human-readable breakdown

**Example:**

```json
{
  "tier": "free",
  "period_start": "",
  "period_end": "",
  "qurls_created": 1,
  "unit_price_cents": 0,
  "active_resources": 1,
  "data_transfer_bytes": 1,
  "cost_estimate": {
    "currency": "usd",
    "amount_cents": 1500,
    "description": "150 qURLs x $0.10/qURL"
  }
}
```

### UsageCurrentPeriodResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`active_qurls` (required)**

    `integer` — Deprecated — use \`active\_resources\`, whose value this mirrors. Despite the name it has always reported the count of active protected resources.

  - **`active_resources` (required)**

    `integer` — Number of currently active protected resources — the target URLs and qURL Connectors you protect.

  - **`data_transfer_bytes` (required)**

    `integer`, format: `int64` — Data proxied through your protected resources in the current UTC calendar month, in bytes. This is independent of the stored Stripe billing period. Reflects traffic measured by the metering pipeline; reads \`0\` where measurement is not yet enabled, and can lag the most recent traffic by a short interval.

  - **`period_end` (required)**

    `string`, format: `date-time` — Exclusive end of the stored Stripe billing period, or exclusive end of the current UTC calendar month when no Stripe period is stored

  - **`period_start` (required)**

    `string`, format: `date-time` — Start of the stored Stripe billing period, or the current UTC calendar month when no Stripe period is stored

  - **`qurls_created` (required)**

    `integer` — Committed metered usage in the stored Stripe billing period, or recorded qURL creations and link mints in the current UTC calendar month when no Stripe period is stored

  - **`tier` (required)**

    `string`, possible values: `"free", "growth", "enterprise"` — Current billing tier

  - **`unit_price_cents` (required)**

    `integer`, format: `int64` — Stored Stripe unit price in cents; zero when no metered price is stored

  - **`cost_estimate`**

    `object`

    - **`amount_cents` (required)**

      `integer`, format: `int64` — Cost in cents

    - **`currency` (required)**

      `string` — Currency code (e.g. "usd")

    - **`description` (required)**

      `string` — Human-readable breakdown

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "tier": "free",
    "period_start": "",
    "period_end": "",
    "qurls_created": 1,
    "unit_price_cents": 0,
    "active_resources": 1,
    "data_transfer_bytes": 1,
    "cost_estimate": {
      "currency": "usd",
      "amount_cents": 1500,
      "description": "150 qURLs x $0.10/qURL"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### UsageDailyEntry

- **Type:**`object`

* **`date` (required)**

  `string`, format: `date` — Calendar date (YYYY-MM-DD)

* **`qurls_created` (required)**

  `integer` — Recorded qURL creations and link mints on this UTC date, including share links. Counts update asynchronously.

**Example:**

```json
{
  "date": "",
  "qurls_created": 1
}
```

### UsageDailyData

- **Type:**`object`

* **`daily` (required)**

  `array` — One entry per UTC date that overlaps the elapsed portion of the half-open billing period

  **Items:**

  - **`date` (required)**

    `string`, format: `date` — Calendar date (YYYY-MM-DD)

  - **`qurls_created` (required)**

    `integer` — Recorded qURL creations and link mints on this UTC date, including share links. Counts update asynchronously.

* **`period_end` (required)**

  `string`, format: `date-time` — Exclusive end of the stored Stripe billing period, or exclusive end of the current UTC calendar month when no Stripe period is stored

* **`period_start` (required)**

  `string`, format: `date-time` — Start of the stored Stripe billing period, or the current UTC calendar month when no Stripe period is stored

* **`tier` (required)**

  `string`, possible values: `"free", "growth", "enterprise"` — Current billing tier

**Example:**

```json
{
  "tier": "free",
  "period_start": "",
  "period_end": "",
  "daily": [
    {
      "date": "",
      "qurls_created": 1
    }
  ]
}
```

### UsageDailyResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`daily` (required)**

    `array` — One entry per UTC date that overlaps the elapsed portion of the half-open billing period

    **Items:**

    - **`date` (required)**

      `string`, format: `date` — Calendar date (YYYY-MM-DD)

    - **`qurls_created` (required)**

      `integer` — Recorded qURL creations and link mints on this UTC date, including share links. Counts update asynchronously.

  - **`period_end` (required)**

    `string`, format: `date-time` — Exclusive end of the stored Stripe billing period, or exclusive end of the current UTC calendar month when no Stripe period is stored

  - **`period_start` (required)**

    `string`, format: `date-time` — Start of the stored Stripe billing period, or the current UTC calendar month when no Stripe period is stored

  - **`tier` (required)**

    `string`, possible values: `"free", "growth", "enterprise"` — Current billing tier

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "tier": "free",
    "period_start": "",
    "period_end": "",
    "daily": [
      {
        "date": "",
        "qurls_created": 1
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### CredentialClaim

- **Type:**`object`

* **`id` (required)**

  `string` — The claimed resource id. For \`type=connector\` this is the connector id (3-64 chars, lowercase a-z0-9 with hyphens, starting with a letter and ending alphanumeric). An id that does not match returns 400 \`invalid\_input\`.

* **`type` (required)**

  `string`, possible values: `"connector"` — The claimed resource type. \`connector\` is the only claim type today; future claim types may be added.

**Example:**

```json
{
  "type": "connector",
  "id": ""
}
```

### CreateApiKeyRequest

- **Type:**`object`

* **`kind` (required)**

  `string`, possible values: `"api_key", "enrollment_token"` — Choose \`api\_key\` for account API access or \`enrollment\_token\` to enroll one device. \`device\` credentials are created by enrollment and cannot be created here. An enrollment token lasts at most 24 hours. Its scopes come from \`target\`. Connector enrollment requires one Connector claim; agent enrollment can omit claims or use one Connector claim. See \`target\` and \`claims\` for the accepted fields. Account setup without a browser requires an emailed verification code before account-key creation; using an enrollment token does not require another emailed code. Use a token for one enrollment at a time. Success consumes it, including finding an existing bound Connector. Validation, conflict, and temporary failures before the action do not consume it. After \`500\`, retry the same enrollment. If the retry returns \`401\`, obtain a new token and check the resource with an account credential. On \`409 bootstrap\_key\_consumed\`, the action can have partly succeeded; use the same recovery.

* **`name` (required)**

  `string` — Human-readable name for the credential

* **`claims`**

  `array` — Resource claims binding a \`kind=enrollment\_token\` credential. At most ONE claim entry is supported today; more return 400 \`invalid\_input\`. A bound token may only enroll against the claimed resource. Not accepted for \`kind=api\_key\` (400 \`invalid\_input\`).

  **Items:**

  - **`id` (required)**

    `string` — The claimed resource id. For \`type=connector\` this is the connector id (3-64 chars, lowercase a-z0-9 with hyphens, starting with a letter and ending alphanumeric). An id that does not match returns 400 \`invalid\_input\`.

  - **`type` (required)**

    `string`, possible values: `"connector"` — The claimed resource type. \`connector\` is the only claim type today; future claim types may be added.

* **`expires_in`**

  `string` — Optional lifetime for \`kind=enrollment\_token\` credentials. Defaults to 24h and cannot exceed 24h. Invalid format or bounds return 400 \`invalid\_duration\`. Not accepted for \`kind=api\_key\` (400 \`invalid\_input\`): API keys are durable.

* **`scopes`**

  `array` — Permissions granted to a \`kind=api\_key\` credential. Required for \`kind=api\_key\`. Requesting \`qurl:agent\` requires the JWT caller to hold \`qurl:agent\`. Must be OMITTED for \`kind=enrollment\_token\`: token scopes are server-assigned from \`target\` (agent -> \`qurl:agent\`; connector -> \`qurl:agent\` + \`qurl:write\`) and sending this field returns 400 \`invalid\_input\`.

  **Items:**

  `string`, possible values: `"qurl:read", "qurl:write", "qurl:resolve", "qurl:agent", "qurl:keys"`

* **`target`**

  `string`, possible values: `"agent", "connector"` — What a \`kind=enrollment\_token\` credential enrolls. When omitted it is derived from \`claims\`: a connector claim implies \`connector\`, otherwise \`agent\`. \`target=connector\` requires exactly one claim of type \`connector\` (the connector id the token is bound to); \`target=agent\` accepts zero claims (unbound) or one connector claim (a bound agent token). Not accepted for \`kind=api\_key\` (400 \`invalid\_input\`).

**Example:**

```json
{
  "kind": "api_key",
  "name": "",
  "scopes": [
    "qurl:read"
  ],
  "target": "agent",
  "claims": [
    {
      "type": "connector",
      "id": ""
    }
  ],
  "expires_in": ""
}
```

### UpdateApiKeyRequest

- **Type:**`object`

* **`name`**

  `string` — Updated name for the credential

* **`scopes`**

  `array` — Updated permissions for the credential. Only \`kind=api\_key\` credentials accept scope updates: enrollment-token scopes are server-assigned and device credentials cannot be edited (both return 400 \`invalid\_input\`). Adding \`qurl:agent\` to a key that does not already hold it requires the JWT caller to hold \`qurl:agent\`. Keeping or removing an existing \`qurl:agent\` scope requires no additional permission beyond \`qurl:write\`. Any replacement set that includes \`qurl:keys\` requires the JWT to hold \`qurl:keys\`, even when that scope is already on the key. Omit this field to leave scopes unchanged. When provided, replaces all existing scopes (not a merge).

  **Items:**

  `string`, possible values: `"qurl:read", "qurl:write", "qurl:resolve", "qurl:agent", "qurl:keys"`

**Example:**

```json
{
  "name": "",
  "scopes": [
    "qurl:read"
  ]
}
```

### ApiKeyData

- **Type:**`object`

* **`claims`**

  `array` — Resource claims bound to a \`kind=enrollment\_token\` credential. Present only on bound enrollment tokens (always exactly one entry today).

  **Items:**

  - **`id` (required)**

    `string` — The claimed resource id. For \`type=connector\` this is the connector id (3-64 chars, lowercase a-z0-9 with hyphens, starting with a letter and ending alphanumeric). An id that does not match returns 400 \`invalid\_input\`.

  - **`type` (required)**

    `string`, possible values: `"connector"` — The claimed resource type. \`connector\` is the only claim type today; future claim types may be added.

* **`created_at`**

  `string`, format: `date-time`

* **`expires_at`**

  `string`, format: `date-time` — Expiration timestamp for enrollment tokens and other expiring credentials. Omitted for non-expiring credentials.

* **`key_id`**

  `string`

* **`key_prefix`**

  `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

* **`kind`**

  `string`, possible values: `"api_key", "enrollment_token", "device"` — The credential kind: a durable \`api\_key\`, a one-shot \`enrollment\_token\` (see \`target\` and \`claims\`), or a system-minted per-device \`device\` credential. Unknown future kind values may appear as the credential model grows; clients must tolerate values outside this enum.

* **`last_used_at`**

  `string`, format: `date-time`

* **`name`**

  `string`

* **`scopes`**

  `array` — Permissions granted to this credential (server-assigned for enrollment tokens and device credentials). Returned in alphabetical order for deterministic responses.

  **Items:**

  `string`

* **`status`**

  `string`, possible values: `"active", "revoked"`

* **`target`**

  `string`, possible values: `"agent", "connector"` — What a \`kind=enrollment\_token\` credential enrolls. Present only on enrollment tokens.

* **`updated_at`**

  `string`, format: `date-time` — Timestamp of the last update to name or scopes. Omitted if the key has never been updated.

**Example:**

```json
{
  "key_id": "",
  "key_prefix": "",
  "kind": "api_key",
  "target": "agent",
  "claims": [
    {
      "type": "connector",
      "id": ""
    }
  ],
  "name": "",
  "scopes": [
    ""
  ],
  "status": "active",
  "created_at": "",
  "updated_at": "",
  "last_used_at": "",
  "expires_at": ""
}
```

### CreateApiKeyData

- **Type:**

**Example:**

### CreateApiKeyResponse

- **Type:**`object`

* **`data`**

  `object`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "key_id": "",
    "key_prefix": "",
    "kind": "api_key",
    "target": "agent",
    "claims": [
      {
        "type": "connector",
        "id": ""
      }
    ],
    "name": "",
    "scopes": [
      ""
    ],
    "status": "active",
    "created_at": "",
    "updated_at": "",
    "last_used_at": "",
    "expires_at": "",
    "api_key": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### ApiKeyResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`claims`**

    `array` — Resource claims bound to a \`kind=enrollment\_token\` credential. Present only on bound enrollment tokens (always exactly one entry today).

    **Items:**

    - **`id` (required)**

      `string` — The claimed resource id. For \`type=connector\` this is the connector id (3-64 chars, lowercase a-z0-9 with hyphens, starting with a letter and ending alphanumeric). An id that does not match returns 400 \`invalid\_input\`.

    - **`type` (required)**

      `string`, possible values: `"connector"` — The claimed resource type. \`connector\` is the only claim type today; future claim types may be added.

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string`, format: `date-time` — Expiration timestamp for enrollment tokens and other expiring credentials. Omitted for non-expiring credentials.

  - **`key_id`**

    `string`

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

  - **`kind`**

    `string`, possible values: `"api_key", "enrollment_token", "device"` — The credential kind: a durable \`api\_key\`, a one-shot \`enrollment\_token\` (see \`target\` and \`claims\`), or a system-minted per-device \`device\` credential. Unknown future kind values may appear as the credential model grows; clients must tolerate values outside this enum.

  - **`last_used_at`**

    `string`, format: `date-time`

  - **`name`**

    `string`

  - **`scopes`**

    `array` — Permissions granted to this credential (server-assigned for enrollment tokens and device credentials). Returned in alphabetical order for deterministic responses.

    **Items:**

    `string`

  - **`status`**

    `string`, possible values: `"active", "revoked"`

  - **`target`**

    `string`, possible values: `"agent", "connector"` — What a \`kind=enrollment\_token\` credential enrolls. Present only on enrollment tokens.

  - **`updated_at`**

    `string`, format: `date-time` — Timestamp of the last update to name or scopes. Omitted if the key has never been updated.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "key_id": "",
    "key_prefix": "",
    "kind": "api_key",
    "target": "agent",
    "claims": [
      {
        "type": "connector",
        "id": ""
      }
    ],
    "name": "",
    "scopes": [
      ""
    ],
    "status": "active",
    "created_at": "",
    "updated_at": "",
    "last_used_at": "",
    "expires_at": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### ApiKeyListResponse

- **Type:**`object`

* **`data`**

  `array`

  **Items:**

  - **`claims`**

    `array` — Resource claims bound to a \`kind=enrollment\_token\` credential. Present only on bound enrollment tokens (always exactly one entry today).

    **Items:**

    - **`id` (required)**

      `string` — The claimed resource id. For \`type=connector\` this is the connector id (3-64 chars, lowercase a-z0-9 with hyphens, starting with a letter and ending alphanumeric). An id that does not match returns 400 \`invalid\_input\`.

    - **`type` (required)**

      `string`, possible values: `"connector"` — The claimed resource type. \`connector\` is the only claim type today; future claim types may be added.

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string`, format: `date-time` — Expiration timestamp for enrollment tokens and other expiring credentials. Omitted for non-expiring credentials.

  - **`key_id`**

    `string`

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

  - **`kind`**

    `string`, possible values: `"api_key", "enrollment_token", "device"` — The credential kind: a durable \`api\_key\`, a one-shot \`enrollment\_token\` (see \`target\` and \`claims\`), or a system-minted per-device \`device\` credential. Unknown future kind values may appear as the credential model grows; clients must tolerate values outside this enum.

  - **`last_used_at`**

    `string`, format: `date-time`

  - **`name`**

    `string`

  - **`scopes`**

    `array` — Permissions granted to this credential (server-assigned for enrollment tokens and device credentials). Returned in alphabetical order for deterministic responses.

    **Items:**

    `string`

  - **`status`**

    `string`, possible values: `"active", "revoked"`

  - **`target`**

    `string`, possible values: `"agent", "connector"` — What a \`kind=enrollment\_token\` credential enrolls. Present only on enrollment tokens.

  - **`updated_at`**

    `string`, format: `date-time` — Timestamp of the last update to name or scopes. Omitted if the key has never been updated.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "key_id": "",
      "key_prefix": "",
      "kind": "api_key",
      "target": "agent",
      "claims": [
        {
          "type": "connector",
          "id": ""
        }
      ],
      "name": "",
      "scopes": [
        ""
      ],
      "status": "active",
      "created_at": "",
      "updated_at": "",
      "last_used_at": "",
      "expires_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### CreateExternalIdentityBindingRequest

- **Type:**`object`

* **`external_id` (required)**

  `string` — Provider-specific stable identifier (Slack \`team\_id\`, Discord \`guild\_id\`, or the canonical Microsoft Entra tenant ID for Teams). qURL treats the value as opaque; canonicalization is the trusted provider flow's responsibility. Only visible ASCII is allowed, and \`#\` is not allowed.

* **`provider` (required)**

  `string`, possible values: `"slack", "discord", "teams"` — Provider that owns the external identity: Slack, Discord, or Microsoft Teams. Unknown values fail request validation. Every provider requires its own configured confidential OAuth client; a request whose verified token presents a different client, or whose provider is not configured, fails with 403.

* **`display_name`**

  `string` — Optional human-readable label for audit/display. An explicit rotation that omits this field preserves the current label.

* **`rotate_existing`**

  `boolean`, default: `false` — Explicitly allow this request to revoke and replace a same-owner binding credential. Omit or set false for first-install-only behavior.

**Example:**

```json
{
  "provider": "slack",
  "external_id": "",
  "display_name": "",
  "rotate_existing": false
}
```

### ExternalIdentityBindingApiKey

- **Type:**`object`

* **`key_id`**

  `string`

* **`key_prefix`**

  `string` — First 12 characters of the key for identification.

* **`plaintext`**

  `string` — The plaintext API key. Returned only once at creation time — store it securely.

**Example:**

```json
{
  "key_id": "",
  "key_prefix": "",
  "plaintext": ""
}
```

### CreateExternalIdentityBindingResponse

- **Type:**`object`

* **`api_key`**

  `object`

  - **`key_id`**

    `string`

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification.

  - **`plaintext`**

    `string` — The plaintext API key. Returned only once at creation time — store it securely.

* **`binding_id`**

  `string` — Unique binding identifier.

* **`created_at`**

  `string`, format: `date-time`

* **`display_name`**

  `string`

* **`external_id`**

  `string` — Opaque provider-specific stable identifier.

* **`provider`**

  `string` — Provider that owns the external identity.

* **`scopes`**

  `array` — Scopes the minted API key was issued with.

  **Items:**

  `string`

**Example:**

```json
{
  "binding_id": "",
  "provider": "",
  "external_id": "",
  "display_name": "",
  "api_key": {
    "key_id": "",
    "key_prefix": "",
    "plaintext": ""
  },
  "scopes": [
    ""
  ],
  "created_at": ""
}
```

### RedeemAccessCodeRequest

- **Type:**`object`

* **`code` (required)**

  `string` — The plaintext access code to redeem

* **`elapsed_ms`**

  `integer` — Milliseconds elapsed since page load (for bot detection)

* **`honeypot`**

  `string`, default: `""` — Honeypot field for bot detection (must be empty)

**Example:**

```json
{
  "code": "ac_k8xqp9h2sj9lx7r4abcdef",
  "honeypot": "",
  "elapsed_ms": 5200
}
```

### RedeemAccessCodeData

- **Type:**`object`

* **`redirect_url`**

  `string`, format: `uri` — URL to redirect the user to for resource access

**Example:**

```json
{
  "redirect_url": "https://qurl.link/#at_k8xqp9h2sj9lx7r4abcdef"
}
```

### RedeemAccessCodeResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`redirect_url`**

    `string`, format: `uri` — URL to redirect the user to for resource access

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "redirect_url": "https://qurl.link/#at_k8xqp9h2sj9lx7r4abcdef"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### CreateAccessCodeRequest

- **Type:**`object`

* **`resource_id` (required)**

  `string` — CRID of the resource to share. The field name remains \`resource\_id\`; a public key is also accepted.

* **`expires_at`**

  `string`, format: `date-time` — Optional expiration time for the access code

* **`max_uses`**

  `integer`, default: `0` — Maximum number of times this code can be redeemed (0 = unlimited)

* **`name`**

  `string` — Human-readable label for the access code

**Example:**

```json
{
  "resource_id": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
  "name": "Investor Stats Q1",
  "max_uses": 10,
  "expires_at": ""
}
```

### AccessCodeData

- **Type:**`object`

* **`access_code_id`**

  `string` — Unique identifier for the access code

* **`created_at`**

  `string`, format: `date-time`

* **`expires_at`**

  `string | null`, format: `date-time`

* **`max_uses`**

  `integer` — Maximum redemptions allowed (0 = unlimited)

* **`name`**

  `string` — Human-readable label

* **`resource_id`**

  `string` — Public key of the resource this code grants access to.

* **`status`**

  `string`, possible values: `"active", "revoked"` — Current status of the access code

* **`use_count`**

  `integer` — Number of times this code has been redeemed

**Example:**

```json
{
  "access_code_id": "acd_k8xqp9h2sj9",
  "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
  "name": "Investor Stats Q1",
  "status": "active",
  "max_uses": 10,
  "use_count": 3,
  "created_at": "",
  "expires_at": null
}
```

### CreateAccessCodeData

- **Type:**

**Example:**

### CreateAccessCodeResponse

- **Type:**`object`

* **`data`**

  `object`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "access_code_id": "acd_k8xqp9h2sj9",
    "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
    "name": "Investor Stats Q1",
    "status": "active",
    "max_uses": 10,
    "use_count": 3,
    "created_at": "",
    "expires_at": null,
    "code": "ac_k8xqp9h2sj9lx7r4abcdef"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### AccessCodeListResponse

- **Type:**`object`

* **`data`**

  `array`

  **Items:**

  - **`access_code_id`**

    `string` — Unique identifier for the access code

  - **`created_at`**

    `string`, format: `date-time`

  - **`expires_at`**

    `string | null`, format: `date-time`

  - **`max_uses`**

    `integer` — Maximum redemptions allowed (0 = unlimited)

  - **`name`**

    `string` — Human-readable label

  - **`resource_id`**

    `string` — Public key of the resource this code grants access to.

  - **`status`**

    `string`, possible values: `"active", "revoked"` — Current status of the access code

  - **`use_count`**

    `integer` — Number of times this code has been redeemed

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": [
    {
      "access_code_id": "acd_k8xqp9h2sj9",
      "resource_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E_S63UMtXba1moR8-Q6EV7oM6zwwh9_j2CDujzXvLA",
      "name": "Investor Stats Q1",
      "status": "active",
      "max_uses": 10,
      "use_count": 3,
      "created_at": "",
      "expires_at": null
    }
  ],
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### WebhookEventType

- **Type:**`string`

Event names are stable API values. Resource events can use a `qurl.*` name for compatibility. Use `crid` to identify the resource and the event's link identifier when present. See `WebhookPayload` for fields and delivery rules.

**Not yet delivered:** `qurl.expired` and `resource.closed`. Subscribing to these events does not enable delivery.

- **qurl.created** - A qURL was created.
- **qurl.expired** - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event.
- **qurl.revoked** - A resource or qURL was manually revoked. Individual link events include `qurl_id`. Revoking a `type=transit` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See `WebhookPayload` for `type=transit` privacy rules.
- **qurl.updated** - Resource metadata or an individual qURL was updated. Individual link updates include `qurl_display_id`.
- **resource.closed** - Not yet delivered. Planned to report that all links have ended and no active sessions remain.
- **qurl.accessed** - Access through a qURL succeeded. Resources with `type=transit` omit viewer IP and user agent. See `WebhookPayload`.
- **consumer.subscribed** - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version.
- **qurl.access\_denied** - Access to a qURL was denied by policy
- **qurl.token\_exhausted** - A one-time-use token was fully consumed
- **quota.warning** - Approaching quota limit (80%)
- **quota.exceeded** - Quota limit reached, action blocked
- **token.minted** - A new access token was created
- **token.expired** - An access token has expired
- **domain.verified** - A custom domain was successfully verified
- **domain.failed** - Custom domain verification or cert provisioning failed
- **domain.deleted** - A custom domain was deleted

**Example:**

### CreateWebhookRequest

- **Type:**`object`

* **`events` (required)**

  `array` — Event types to subscribe to

  **Items:**

  `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

* **`url` (required)**

  `string`, format: `uri` — HTTPS endpoint URL to receive webhook events (max 2048 characters)

* **`description`**

  `string` — Human-readable description

**Example:**

```json
{
  "url": "https://example.com/webhooks/qurl",
  "events": [
    "qurl.created"
  ],
  "description": ""
}
```

### UpdateWebhookRequest

- **Type:**`object`

* **`description`**

  `string`

* **`events`**

  `array`

  **Items:**

  `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

* **`status`**

  `string`, possible values: `"active", "disabled"`

* **`url`**

  `string`, format: `uri`

**Example:**

```json
{
  "url": "",
  "events": [
    "qurl.created"
  ],
  "description": "",
  "status": "active"
}
```

### WebhookData

- **Type:**`object`

* **`created_at`**

  `string`, format: `date-time`

* **`description`**

  `string`

* **`events`**

  `array`

  **Items:**

  `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

* **`failure_count`**

  `integer` — Consecutive delivery failures

* **`last_delivery_success`**

  `boolean`

* **`last_delivery_time`**

  `integer` — Unix timestamp of last delivery attempt

* **`owner_id`**

  `string` — Identifier of the account that owns this subscription. Bound at create time from the authenticating API key and immutable thereafter.

* **`status`**

  `string`, possible values: `"active", "disabled"`

* **`updated_at`**

  `string`, format: `date-time`

* **`url`**

  `string`, format: `uri`

* **`webhook_id`**

  `string`

**Example:**

```json
{
  "webhook_id": "",
  "owner_id": "auth0|user123",
  "url": "",
  "events": [
    "qurl.created"
  ],
  "status": "active",
  "description": "",
  "created_at": "",
  "updated_at": "",
  "failure_count": 1,
  "last_delivery_success": true,
  "last_delivery_time": 1
}
```

### WebhookDataWithSecret

- **Type:**

**Example:**

### WebhookResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`created_at`**

    `string`, format: `date-time`

  - **`description`**

    `string`

  - **`events`**

    `array`

    **Items:**

    `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

  - **`failure_count`**

    `integer` — Consecutive delivery failures

  - **`last_delivery_success`**

    `boolean`

  - **`last_delivery_time`**

    `integer` — Unix timestamp of last delivery attempt

  - **`owner_id`**

    `string` — Identifier of the account that owns this subscription. Bound at create time from the authenticating API key and immutable thereafter.

  - **`status`**

    `string`, possible values: `"active", "disabled"`

  - **`updated_at`**

    `string`, format: `date-time`

  - **`url`**

    `string`, format: `uri`

  - **`webhook_id`**

    `string`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "owner_id": "auth0|user123",
    "url": "",
    "events": [
      "qurl.created"
    ],
    "status": "active",
    "description": "",
    "created_at": "",
    "updated_at": "",
    "failure_count": 1,
    "last_delivery_success": true,
    "last_delivery_time": 1
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### WebhookResponseWithSecret

- **Type:**`object`

* **`data`**

  `object`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "owner_id": "auth0|user123",
    "url": "",
    "events": [
      "qurl.created"
    ],
    "status": "active",
    "description": "",
    "created_at": "",
    "updated_at": "",
    "failure_count": 1,
    "last_delivery_success": true,
    "last_delivery_time": 1,
    "secret": "whsec_K8xQp9H2sJ9Lx7R4AaBbCcDdEeFf..."
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### WebhookListResponse

- **Type:**`object`

* **`data`**

  `array`

  **Items:**

  - **`created_at`**

    `string`, format: `date-time`

  - **`description`**

    `string`

  - **`events`**

    `array`

    **Items:**

    `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

  - **`failure_count`**

    `integer` — Consecutive delivery failures

  - **`last_delivery_success`**

    `boolean`

  - **`last_delivery_time`**

    `integer` — Unix timestamp of last delivery attempt

  - **`owner_id`**

    `string` — Identifier of the account that owns this subscription. Bound at create time from the authenticating API key and immutable thereafter.

  - **`status`**

    `string`, possible values: `"active", "disabled"`

  - **`updated_at`**

    `string`, format: `date-time`

  - **`url`**

    `string`, format: `uri`

  - **`webhook_id`**

    `string`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "webhook_id": "",
      "owner_id": "auth0|user123",
      "url": "",
      "events": [
        "qurl.created"
      ],
      "status": "active",
      "description": "",
      "created_at": "",
      "updated_at": "",
      "failure_count": 1,
      "last_delivery_success": true,
      "last_delivery_time": 1
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### WebhookDeliveryData

- **Type:**`object`

* **`completed_at`**

  `string`, format: `date-time` — Time the most recent delivery attempt completed, including an attempt that will be retried.

* **`created_at`**

  `string`, format: `date-time`

* **`delivery_id`**

  `string`

* **`duration_ms`**

  `integer`

* **`error_message`**

  `string`

* **`event_type`**

  `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

* **`response_body`**

  `string` — Response body limited to 4096 bytes plus a truncation marker.

* **`response_code`**

  `integer`

* **`retry_count`**

  `integer` — Persisted attempt claims, including the first attempt. Older delivery rows counted failed attempts.

* **`status`**

  `string`, possible values: `"pending", "success", "failed", "retrying", "abandoned"` — Retrying includes durable deliveries waiting for their first attempt or held by an active attempt lease.

* **`webhook_id`**

  `string`

**Example:**

```json
{
  "delivery_id": "",
  "webhook_id": "",
  "event_type": "qurl.created",
  "status": "pending",
  "response_code": 1,
  "response_body": "",
  "error_message": "",
  "duration_ms": 1,
  "retry_count": 1,
  "created_at": "",
  "completed_at": ""
}
```

### WebhookDeliveryListResponse

- **Type:**`object`

* **`data`**

  `array`

  **Items:**

  - **`completed_at`**

    `string`, format: `date-time` — Time the most recent delivery attempt completed, including an attempt that will be retried.

  - **`created_at`**

    `string`, format: `date-time`

  - **`delivery_id`**

    `string`

  - **`duration_ms`**

    `integer`

  - **`error_message`**

    `string`

  - **`event_type`**

    `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

  - **`response_body`**

    `string` — Response body limited to 4096 bytes plus a truncation marker.

  - **`response_code`**

    `integer`

  - **`retry_count`**

    `integer` — Persisted attempt claims, including the first attempt. Older delivery rows counted failed attempts.

  - **`status`**

    `string`, possible values: `"pending", "success", "failed", "retrying", "abandoned"` — Retrying includes durable deliveries waiting for their first attempt or held by an active attempt lease.

  - **`webhook_id`**

    `string`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "delivery_id": "",
      "webhook_id": "",
      "event_type": "qurl.created",
      "status": "pending",
      "response_code": 1,
      "response_body": "",
      "error_message": "",
      "duration_ms": 1,
      "retry_count": 1,
      "created_at": "",
      "completed_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### WebhookEventTypeInfo

- **Type:**`object`

* **`category`**

  `string`, possible values: `"resource", "access", "quota", "token"`

* **`description`**

  `string`

* **`type`**

  `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

**Example:**

```json
{
  "type": "qurl.created",
  "category": "resource",
  "description": ""
}
```

### WebhookEventTypesResponse

- **Type:**`object`

* **`data`**

  `array`

  **Items:**

  - **`category`**

    `string`, possible values: `"resource", "access", "quota", "token"`

  - **`description`**

    `string`

  - **`type`**

    `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": [
    {
      "type": "qurl.created",
      "category": "resource",
      "description": ""
    }
  ],
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### WebhookPayload

- **Type:**`object`

Event sent to your webhook endpoint. Verify `QURL-Signature` before processing it. See `WebhookEventType` for availability; subscribing to a future event does not enable delivery.

For `type=transit` resources, the installer and viewer can be different people. The following rules protect viewer and uploaded-content data:

| Event             | Delivery for `type=transit`                                                                                |
| ----------------- | ---------------------------------------------------------------------------------------------------------- |
| `qurl.accessed`   | Delivered without `src_ip` and `user_agent`.                                                               |
| `qurl.revoked`    | Delivered with `qurl_id` and `resource_id`; omits target URL, description, tags, and filename.             |
| `qurl.expired`    | Payload contract includes `qurl_id`, `resource_id`, and `expires_at`; delivery begins in a future release. |
| `resource.closed` | Payload contract includes `resource_id`; delivery begins in a future release.                              |
| All other events  | Not delivered, including `qurl.created`, `qurl.updated`, and `token.minted`.                               |

Events with `resource_id` also include `crid` when available. Prefer `crid` to identify the resource; retain `qurl_id` to identify one link.

Revoking a URL or tunnel resource sends one resource-scoped `qurl.revoked` event. Revoking a `type=transit` resource sends at most 100 individual events for active links. No active links means no individual revoke events. Delivery can be partial due to the cap, degraded operation, or publish failures. Concurrent changes can duplicate or omit events. A revoked parent can leave child link records with `status: active`; the links still cannot grant access. Check the current resource state when you need to confirm access status. See `data` for fields and payload examples.

- **`api_version`**

  `string` — API version (e.g., "2024-01-01")

- **`data`**

  `object` — Fields depend on the event and resource type. Events with \`resource\_id\` also include \`crid\` when available. Use the CRID for later resource requests; the \`resource\_id\` value is a compatibility public key. ### \`qurl.accessed\` | Field | Meaning | |---|---| | \`qurl\_id\` | The link used for access. | | \`resource\_id\` | The protected resource's public key. | | \`access\_count\` | Count after this access. Concurrent events can show the same count; use the resource's current link summaries to check later state. | | \`consumed\` | Whether this access consumed a one-time link. | | \`src\_ip\`, \`user\_agent\` | Viewer information. Omitted for integration-owned uploads (\`type=transit\`). | #### \`data\` shape examples URL / qURL Connector resource (full payload): \`\`\`json { "qurl\_id": "q\_3a7f2c8e91b", "resource\_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5\_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E\_S63UMtXba1moR8-Q6EV7oM6zwwh9\_j2CDujzXvLA", "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a", "access\_count": 3, "consumed": false, "src\_ip": "192.168.1.100", "user\_agent": "Mozilla/5.0..." } \`\`\` Transit (connector-owned) resource — \`src\_ip\` and \`user\_agent\` omitted: \`\`\`json { "qurl\_id": "q\_4b8e3d9f02c", "resource\_id": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cTVv5\_3eeYCcLLq5ROYCqcmY50HiKZ9ATglIkPnCji1E\_S63UMtXba1moR8-Q6EV7oM6zwwh9\_j2CDujzXvLA", "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a", "access\_count": 7, "consumed": false } \`\`\` ### \`qurl.updated\` Resource updates identify the resource with \`resource\_id\` and, when available, \`crid\`. Individual link updates also include \`qurl\_display\_id\`, the link's qURL ID. Do not require a \`qurl\_id\` field for this event. ### \`qurl.revoked\` | Field | When present | |---|---| | \`resource\_id\` | Always. | | \`qurl\_id\` | Individual link revokes and all delivered integration-owned upload revokes. | | \`qurl\_display\_id\` | Compatibility alias for \`qurl\_id\` on individual URL and qURL Connector link revokes. | | \`target\_url\` | Resource-wide URL and qURL Connector revokes. Omitted for integration-owned uploads. | Integration-owned upload events also omit description, tags, and filename. Revoking a \`type=transit\` resource can produce a partial set of individual events. See \`WebhookPayload\` for the 100-event limit, concurrent changes, and how to check access status after revocation.

- **`id`**

  `string` — Unique event ID

- **`owner_id`**

  `string` — Owner who triggered the event

- **`timestamp`**

  `string`, format: `date-time`

- **`type`**

  `string`, possible values: `"qurl.created", "qurl.expired", "qurl.revoked", "qurl.updated", "resource.closed", "qurl.accessed", "consumer.subscribed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Event names are stable API values. Resource events can use a \`qurl.\*\` name for compatibility. Use \`crid\` to identify the resource and the event's link identifier when present. See \`WebhookPayload\` for fields and delivery rules. \*\*Not yet delivered:\*\* \`qurl.expired\` and \`resource.closed\`. Subscribing to these events does not enable delivery. - \*\*qurl.created\*\* - A qURL was created. - \*\*qurl.expired\*\* - Not yet delivered. Planned to report expiration at most once per link lifetime, including revoked or consumed links. Extending a link after this event has fired does not cause another event. - \*\*qurl.revoked\*\* - A resource or qURL was manually revoked. Individual link events include \`qurl\_id\`. Revoking a \`type=transit\` resource sends at most 100 individual link events and can be partial. Revoking a URL or tunnel resource sends one resource event. Check resource state to confirm access status. See \`WebhookPayload\` for \`type=transit\` privacy rules. - \*\*qurl.updated\*\* - Resource metadata or an individual qURL was updated. Individual link updates include \`qurl\_display\_id\`. - \*\*resource.closed\*\* - Not yet delivered. Planned to report that all links have ended and no active sessions remain. - \*\*qurl.accessed\*\* - Access through a qURL succeeded. Resources with \`type=transit\` omit viewer IP and user agent. See \`WebhookPayload\`. - \*\*consumer.subscribed\*\* - A publisher backend confirmed a contract entitlement. Contains a resource-scoped consumer reference, policy and grant versions, contract and expiry; omits consumer email and credential identity. Deduplicate by resource, consumer\_reference and grant\_version. - \*\*qurl.access\_denied\*\* - Access to a qURL was denied by policy - \*\*qurl.token\_exhausted\*\* - A one-time-use token was fully consumed - \*\*quota.warning\*\* - Approaching quota limit (80%) - \*\*quota.exceeded\*\* - Quota limit reached, action blocked - \*\*token.minted\*\* - A new access token was created - \*\*token.expired\*\* - An access token has expired - \*\*domain.verified\*\* - A custom domain was successfully verified - \*\*domain.failed\*\* - Custom domain verification or cert provisioning failed - \*\*domain.deleted\*\* - A custom domain was deleted

**Example:**

```json
{
  "id": "evt_abc123def456",
  "type": "qurl.accessed",
  "owner_id": "auth0|user123",
  "timestamp": "2024-01-08T10:35:00Z",
  "api_version": "2024-01-01",
  "data": {
    "qurl_id": "q_3a7f2c8e91b",
    "crid": "ahpviqz46qwcvx56glfatm3p3ooccwfcf2it4sdgjervwdkapykw3o3qdq2a",
    "access_count": 3,
    "consumed": false,
    "src_ip": "192.168.1.100",
    "user_agent": "Mozilla/5.0..."
  }
}
```

### Publisher

- **Type:**`object`

Who published a resource: the owner of the account or device that registered it.

`verified` is `true` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. `verified: false` means the publisher's `name` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If `verified` is ever missing, treat it as `false`.

- **`verified` (required)**

  `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

- **`name`**

  `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

**Example:**

```json
{
  "name": "Acme Docs",
  "verified": false
}
```

### PreviewMedia

- **Type:**`object`

* **`alt`**

  `string`

* **`clip`**

  `string`, format: `uri`

* **`cover`**

  `string`, format: `uri`

* **`fallback`**

  `string`, format: `uri`

* **`height`**

  `integer`

* **`id`**

  `string`

* **`image`**

  `string`, format: `uri`

* **`kind`**

  `string`, possible values: `"image", "video"`

* **`width`**

  `integer`

**Example:**

```json
{
  "id": "",
  "kind": "image",
  "alt": "",
  "width": 1,
  "height": 1,
  "cover": "",
  "fallback": "",
  "image": "",
  "clip": ""
}
```

### PreviewUpload

- **Type:**`object`

* **`alt`**

  `string`

* **`content_type`**

  `string`

* **`expires_at`**

  `integer`, format: `int64`

* **`id`**

  `string`

* **`replace_id`**

  `string`

* **`size`**

  `integer`, format: `int64`

* **`status`**

  `string`, possible values: `"processing", "failed"`

**Example:**

```json
{
  "id": "",
  "replace_id": "",
  "alt": "",
  "content_type": "",
  "size": 1,
  "expires_at": 1,
  "status": "processing"
}
```

### PreviewCard

- **Type:**`object`

* **`description`**

  `string`

* **`media`**

  `array`

  **Items:**

  - **`alt`**

    `string`

  - **`clip`**

    `string`, format: `uri`

  - **`cover`**

    `string`, format: `uri`

  - **`fallback`**

    `string`, format: `uri`

  - **`height`**

    `integer`

  - **`id`**

    `string`

  - **`image`**

    `string`, format: `uri`

  - **`kind`**

    `string`, possible values: `"image", "video"`

  - **`width`**

    `integer`

* **`name`**

  `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

**Example:**

```json
{
  "name": "",
  "description": "",
  "media": [
    {
      "id": "",
      "kind": "image",
      "alt": "",
      "width": 1,
      "height": 1,
      "cover": "",
      "fallback": "",
      "image": "",
      "clip": ""
    }
  ]
}
```

### ResourcePreview

- **Type:**`object`

* **`card`**

  `object`

  - **`description`**

    `string`

  - **`media`**

    `array`

    **Items:**

    - **`alt`**

      `string`

    - **`clip`**

      `string`, format: `uri`

    - **`cover`**

      `string`, format: `uri`

    - **`fallback`**

      `string`, format: `uri`

    - **`height`**

      `integer`

    - **`id`**

      `string`

    - **`image`**

      `string`, format: `uri`

    - **`kind`**

      `string`, possible values: `"image", "video"`

    - **`width`**

      `integer`

  - **`name`**

    `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

* **`description`**

  `string`

* **`media`**

  `array`

  **Items:**

  - **`alt`**

    `string`

  - **`clip`**

    `string`, format: `uri`

  - **`cover`**

    `string`, format: `uri`

  - **`fallback`**

    `string`, format: `uri`

  - **`height`**

    `integer`

  - **`id`**

    `string`

  - **`image`**

    `string`, format: `uri`

  - **`kind`**

    `string`, possible values: `"image", "video"`

  - **`width`**

    `integer`

* **`name`**

  `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

* **`revision`**

  `integer`, format: `int64`

* **`uploads`**

  `array`

  **Items:**

  - **`alt`**

    `string`

  - **`content_type`**

    `string`

  - **`expires_at`**

    `integer`, format: `int64`

  - **`id`**

    `string`

  - **`replace_id`**

    `string`

  - **`size`**

    `integer`, format: `int64`

  - **`status`**

    `string`, possible values: `"processing", "failed"`

**Example:**

```json
{
  "revision": 0,
  "name": "",
  "description": "",
  "media": [
    {
      "id": "",
      "kind": "image",
      "alt": "",
      "width": 1,
      "height": 1,
      "cover": "",
      "fallback": "",
      "image": "",
      "clip": ""
    }
  ],
  "card": {
    "name": "",
    "description": "",
    "media": [
      {
        "id": "",
        "kind": "image",
        "alt": "",
        "width": 1,
        "height": 1,
        "cover": "",
        "fallback": "",
        "image": "",
        "clip": ""
      }
    ]
  },
  "uploads": [
    {
      "id": "",
      "replace_id": "",
      "alt": "",
      "content_type": "",
      "size": 1,
      "expires_at": 1,
      "status": "processing"
    }
  ]
}
```

### PreviewPatch

- **Type:**`object`

* **`revision` (required)**

  `integer`, format: `int64`

* **`description`**

  `string`

* **`media_ids`**

  `array`

  **Items:**

  `string`

* **`name`**

  `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

**Example:**

```json
{
  "revision": 0,
  "name": "",
  "description": "",
  "media_ids": [
    ""
  ]
}
```

### PreviewUploadRequest

- **Type:**`object`

* **`alt` (required)**

  `string`

* **`content_type` (required)**

  `string`, possible values: `"image/jpeg", "image/png", "image/webp", "image/gif", "video/mp4", "video/webm"`

* **`revision` (required)**

  `integer`, format: `int64`

* **`size` (required)**

  `integer`, format: `int64`

* **`replace_id`**

  `string`

**Example:**

```json
{
  "revision": 0,
  "content_type": "image/jpeg",
  "size": 1,
  "alt": "",
  "replace_id": ""
}
```

### PreviewUploadResponse

- **Type:**`object`

* **`headers` (required)**

  `object`

* **`preview` (required)**

  `object`

  - **`card`**

    `object`

    - **`description`**

      `string`

    - **`media`**

      `array`

      **Items:**

      - **`alt`**

        `string`

      - **`clip`**

        `string`, format: `uri`

      - **`cover`**

        `string`, format: `uri`

      - **`fallback`**

        `string`, format: `uri`

      - **`height`**

        `integer`

      - **`id`**

        `string`

      - **`image`**

        `string`, format: `uri`

      - **`kind`**

        `string`, possible values: `"image", "video"`

      - **`width`**

        `integer`

    - **`name`**

      `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

  - **`description`**

    `string`

  - **`media`**

    `array`

    **Items:**

    - **`alt`**

      `string`

    - **`clip`**

      `string`, format: `uri`

    - **`cover`**

      `string`, format: `uri`

    - **`fallback`**

      `string`, format: `uri`

    - **`height`**

      `integer`

    - **`id`**

      `string`

    - **`image`**

      `string`, format: `uri`

    - **`kind`**

      `string`, possible values: `"image", "video"`

    - **`width`**

      `integer`

  - **`name`**

    `string` — Public single-line app title, without surrounding whitespace. Publisher-provided text, never verified identity. Empty removes it on edits.

  - **`revision`**

    `integer`, format: `int64`

  - **`uploads`**

    `array`

    **Items:**

    - **`alt`**

      `string`

    - **`content_type`**

      `string`

    - **`expires_at`**

      `integer`, format: `int64`

    - **`id`**

      `string`

    - **`replace_id`**

      `string`

    - **`size`**

      `integer`, format: `int64`

    - **`status`**

      `string`, possible values: `"processing", "failed"`

* **`upload_id` (required)**

  `string`

* **`url` (required)**

  `string`, format: `uri`

**Example:**

```json
{
  "preview": {
    "revision": 0,
    "name": "",
    "description": "",
    "media": [
      {
        "id": "",
        "kind": "image",
        "alt": "",
        "width": 1,
        "height": 1,
        "cover": "",
        "fallback": "",
        "image": "",
        "clip": ""
      }
    ],
    "card": {
      "name": "",
      "description": "",
      "media": [
        {
          "id": "",
          "kind": "image",
          "alt": "",
          "width": 1,
          "height": 1,
          "cover": "",
          "fallback": "",
          "image": "",
          "clip": ""
        }
      ]
    },
    "uploads": [
      {
        "id": "",
        "replace_id": "",
        "alt": "",
        "content_type": "",
        "size": 1,
        "expires_at": 1,
        "status": "processing"
      }
    ]
  },
  "upload_id": "",
  "url": "",
  "headers": {
    "additionalProperty": ""
  }
}
```

### PublisherResponse

- **Type:**`object`

* **`data`**

  `object` — Who published a resource: the owner of the account or device that registered it. \`verified\` is \`true\` only when qURL has confirmed who the publisher is. Customers cannot apply for verification yet, so most publishers are not verified. \`verified: false\` means the publisher's \`name\` is self-declared: the publisher chose it, and nobody has confirmed that it is accurate. Label it as unverified wherever you show it, and do not treat it as proof of who published a resource. If \`verified\` is ever missing, treat it as \`false\`.

  - **`verified` (required)**

    `boolean` — Whether qURL has confirmed who this publisher is. qURL decides it; a publisher cannot set it. \`false\` means the name is self-declared and has not been confirmed.

  - **`name`**

    `string` — The name the publisher chose for itself. Omitted when the publisher has not set one; never an empty string. It is self-declared unless \`verified\` is \`true\`. Display it as untrusted text: quote it and escape it rather than printing it raw.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "name": "Acme Docs",
    "verified": false
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### UpdatePublisherRequest

- **Type:**`object`

* **`name` (required)**

  `string` — The publisher name to show, or an empty string to remove it. See the operation description for the rules a name must follow.

**Example:**

```json
{
  "name": "Acme Docs"
}
```

### SignupAttributionRequest

- **Type:**`object`

* **`source` (required)**

  `string`, possible values: `"my_ai_told_me", "ai_search", "web_search", "friend_or_coworker", "social_media", "other"` — How you discovered qURL.

**Example:**

```json
{
  "source": "my_ai_told_me"
}
```

### EmailPreferenceSource

- **Type:**`string`

Where an email choice was made. `signup_survey` can only save the first choice; `dashboard` can always save.

**Example:**

### EmailPreferences

- **Type:**`object`

* **`product_updates` (required)**

  `boolean` — Whether to send email about new features and product changes.

* **`promotions` (required)**

  `boolean` — Whether to send announcements, events and offers.

* **`source` (required)**

  `string | null`, possible values: `"signup_survey", "dashboard", null` — Where the email choice was last saved, or null if it never was.

* **`updated_at` (required)**

  `string | null`, format: `date-time` — When the email choice was last saved, or null if it never was.

**Example:**

```json
{
  "product_updates": true,
  "promotions": true,
  "updated_at": null,
  "source": "signup_survey"
}
```

### Preferences

- **Type:**`object`

* **`email` (required)**

  `object`

  - **`product_updates` (required)**

    `boolean` — Whether to send email about new features and product changes.

  - **`promotions` (required)**

    `boolean` — Whether to send announcements, events and offers.

  - **`source` (required)**

    `string | null`, possible values: `"signup_survey", "dashboard", null` — Where the email choice was last saved, or null if it never was.

  - **`updated_at` (required)**

    `string | null`, format: `date-time` — When the email choice was last saved, or null if it never was.

**Example:**

```json
{
  "email": {
    "product_updates": true,
    "promotions": true,
    "updated_at": null,
    "source": "signup_survey"
  }
}
```

### PreferencesResponse

- **Type:**`object`

* **`data` (required)**

  `object`

  - **`email` (required)**

    `object`

    - **`product_updates` (required)**

      `boolean` — Whether to send email about new features and product changes.

    - **`promotions` (required)**

      `boolean` — Whether to send announcements, events and offers.

    - **`source` (required)**

      `string | null`, possible values: `"signup_survey", "dashboard", null` — Where the email choice was last saved, or null if it never was.

    - **`updated_at` (required)**

      `string | null`, format: `date-time` — When the email choice was last saved, or null if it never was.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "email": {
      "product_updates": true,
      "promotions": true,
      "updated_at": null,
      "source": "signup_survey"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### UpdateEmailPreferences

- **Type:**`object`

At least one of `product_updates` and `promotions` is required.

- **`source` (required)**

  `string`, possible values: `"signup_survey", "dashboard"` — Where an email choice was made. \`signup\_survey\` can only save the first choice; \`dashboard\` can always save.

- **`product_updates`**

  `boolean`

- **`promotions`**

  `boolean`

**Example:**

```json
{
  "product_updates": true,
  "promotions": true,
  "source": "signup_survey"
}
```

### UpdatePreferencesRequest

- **Type:**`object`

* **`email` (required)**

  `object` — At least one of \`product\_updates\` and \`promotions\` is required.

  - **`source` (required)**

    `string`, possible values: `"signup_survey", "dashboard"` — Where an email choice was made. \`signup\_survey\` can only save the first choice; \`dashboard\` can always save.

  - **`product_updates`**

    `boolean`

  - **`promotions`**

    `boolean`

**Example:**

```json
{
  "email": {
    "product_updates": true,
    "promotions": true,
    "source": "signup_survey"
  }
}
```

### CustomerData

- **Type:**`object`

* **`current_period_usage` (required)**

  `integer`, format: `int64` — Usage count in current billing period

* **`frozen` (required)**

  `boolean` — Whether the account is frozen

* **`signup_discovery_source` (required)**

  `string | null` — Saved signup discovery source, or null if no answer has been recorded.

* **`spending_cap_cents` (required)**

  `integer`, format: `int64` — Spending cap in cents (0 = no cap)

* **`tier` (required)**

  `string`, possible values: `"free", "growth", "enterprise"` — Customer's billing tier

* **`frozen_reason`**

  `string | null`, possible values: `"spending_cap", "payment_failed", "manual"` — Reason for account freeze

**Example:**

```json
{
  "signup_discovery_source": null,
  "tier": "free",
  "spending_cap_cents": 1,
  "current_period_usage": 1,
  "frozen": true,
  "frozen_reason": "spending_cap"
}
```

### CustomerResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`current_period_usage` (required)**

    `integer`, format: `int64` — Usage count in current billing period

  - **`frozen` (required)**

    `boolean` — Whether the account is frozen

  - **`signup_discovery_source` (required)**

    `string | null` — Saved signup discovery source, or null if no answer has been recorded.

  - **`spending_cap_cents` (required)**

    `integer`, format: `int64` — Spending cap in cents (0 = no cap)

  - **`tier` (required)**

    `string`, possible values: `"free", "growth", "enterprise"` — Customer's billing tier

  - **`frozen_reason`**

    `string | null`, possible values: `"spending_cap", "payment_failed", "manual"` — Reason for account freeze

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "signup_discovery_source": null,
    "tier": "free",
    "spending_cap_cents": 1,
    "current_period_usage": 1,
    "frozen": true,
    "frozen_reason": "spending_cap"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### UpdateCustomerRequest

- **Type:**`object`

* **`spending_cap_cents` (required)**

  `integer`, format: `int64` — New spending cap in cents (0 = no cap)

**Example:**

```json
{
  "spending_cap_cents": 0
}
```

### CreateBillingCheckoutRequest

- **Type:**`object`

* **`plan` (required)**

  `string`, possible values: `"growth"` — Paid plan to check out into. Only purchasable plans are permitted (currently: \`growth\`); anything else is rejected as an invalid enum value.

**Example:**

```json
{
  "plan": "growth"
}
```

### CheckoutSessionData

- **Type:**`object`

* **`url` (required)**

  `string`, format: `uri` — Stripe Checkout URL to redirect user to

**Example:**

```json
{
  "url": ""
}
```

### CheckoutSessionResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`url` (required)**

    `string`, format: `uri` — Stripe Checkout URL to redirect user to

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "url": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### PortalSessionData

- **Type:**`object`

* **`url` (required)**

  `string`, format: `uri` — Stripe Billing Portal URL to redirect user to

**Example:**

```json
{
  "url": ""
}
```

### PortalSessionResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`url` (required)**

    `string`, format: `uri` — Stripe Billing Portal URL to redirect user to

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "url": ""
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### InvoiceData

- **Type:**`object`

* **`amount_cents` (required)**

  `integer`, format: `int64` — Invoice amount in cents

* **`created_at` (required)**

  `string`, format: `date-time` — When the invoice was created

* **`id` (required)**

  `string` — Stripe invoice ID

* **`status` (required)**

  `string`, possible values: `"paid", "open", "void", "draft"` — Invoice status

* **`pdf_url`**

  `string | null`, format: `uri` — URL to download the invoice PDF

**Example:**

```json
{
  "id": "",
  "amount_cents": 1,
  "status": "paid",
  "created_at": "",
  "pdf_url": null
}
```

### InvoiceListData

- **Type:**`object`

* **`invoices` (required)**

  `array`

  **Items:**

  - **`amount_cents` (required)**

    `integer`, format: `int64` — Invoice amount in cents

  - **`created_at` (required)**

    `string`, format: `date-time` — When the invoice was created

  - **`id` (required)**

    `string` — Stripe invoice ID

  - **`status` (required)**

    `string`, possible values: `"paid", "open", "void", "draft"` — Invoice status

  - **`pdf_url`**

    `string | null`, format: `uri` — URL to download the invoice PDF

**Example:**

```json
{
  "invoices": [
    {
      "id": "",
      "amount_cents": 1,
      "status": "paid",
      "created_at": "",
      "pdf_url": null
    }
  ]
}
```

### InvoiceListResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`invoices` (required)**

    `array`

    **Items:**

    - **`amount_cents` (required)**

      `integer`, format: `int64` — Invoice amount in cents

    - **`created_at` (required)**

      `string`, format: `date-time` — When the invoice was created

    - **`id` (required)**

      `string` — Stripe invoice ID

    - **`status` (required)**

      `string`, possible values: `"paid", "open", "void", "draft"` — Invoice status

    - **`pdf_url`**

      `string | null`, format: `uri` — URL to download the invoice PDF

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": {
    "invoices": [
      {
        "id": "",
        "amount_cents": 1,
        "status": "paid",
        "created_at": "",
        "pdf_url": null
      }
    ]
  },
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### RegisterDomainRequest

- **Type:**`object`

* **`domain` (required)**

  `string` — The custom domain name to register

**Example:**

```json
{
  "domain": "secure.example.com"
}
```

### DomainData

- **Type:**`object`

* **`acme_cname_target`**

  `string` — CNAME target for ACME certificate provisioning

* **`activated_at`**

  `string | null`, format: `date-time`

* **`created_at`**

  `string`, format: `date-time`

* **`dns_records`**

  `array` — Required DNS records for domain setup

  **Items:**

  - **`name`**

    `string` — DNS record name

  - **`type`**

    `string` — DNS record type (TXT, CNAME)

  - **`value`**

    `string` — DNS record value

  - **`verified`**

    `boolean` — Whether this record has been verified

* **`domain`**

  `string` — The custom domain name

* **`ready_for_qurls`**

  `boolean` — Whether this domain is ready to be used with qURLs (status is verified, provisioning\_tls, or active)

* **`status`**

  `string`, possible values: `"pending_verification", "verified", "provisioning_tls", "active", "failed"` — Current domain status

* **`token_expires_at`**

  `string | null`, format: `date-time` — When the verification token expires. Omitted for domains created before token expiry was introduced (those tokens never expire). After expiry, use the regenerate-token endpoint to get a new one.

* **`verification_token`**

  `string` — TXT record value for DNS verification

* **`verified_at`**

  `string | null`, format: `date-time`

**Example:**

```json
{
  "domain": "",
  "status": "pending_verification",
  "verification_token": "",
  "token_expires_at": null,
  "acme_cname_target": "",
  "created_at": "",
  "verified_at": null,
  "activated_at": null,
  "ready_for_qurls": true,
  "dns_records": [
    {
      "type": "",
      "name": "",
      "value": "",
      "verified": true
    }
  ]
}
```

### DnsRecord

- **Type:**`object`

* **`name`**

  `string` — DNS record name

* **`type`**

  `string` — DNS record type (TXT, CNAME)

* **`value`**

  `string` — DNS record value

* **`verified`**

  `boolean` — Whether this record has been verified

**Example:**

```json
{
  "type": "",
  "name": "",
  "value": "",
  "verified": true
}
```

### DomainResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`acme_cname_target`**

    `string` — CNAME target for ACME certificate provisioning

  - **`activated_at`**

    `string | null`, format: `date-time`

  - **`created_at`**

    `string`, format: `date-time`

  - **`dns_records`**

    `array` — Required DNS records for domain setup

    **Items:**

    - **`name`**

      `string` — DNS record name

    - **`type`**

      `string` — DNS record type (TXT, CNAME)

    - **`value`**

      `string` — DNS record value

    - **`verified`**

      `boolean` — Whether this record has been verified

  - **`domain`**

    `string` — The custom domain name

  - **`ready_for_qurls`**

    `boolean` — Whether this domain is ready to be used with qURLs (status is verified, provisioning\_tls, or active)

  - **`status`**

    `string`, possible values: `"pending_verification", "verified", "provisioning_tls", "active", "failed"` — Current domain status

  - **`token_expires_at`**

    `string | null`, format: `date-time` — When the verification token expires. Omitted for domains created before token expiry was introduced (those tokens never expire). After expiry, use the regenerate-token endpoint to get a new one.

  - **`verification_token`**

    `string` — TXT record value for DNS verification

  - **`verified_at`**

    `string | null`, format: `date-time`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "domain": "",
    "status": "pending_verification",
    "verification_token": "",
    "token_expires_at": null,
    "acme_cname_target": "",
    "created_at": "",
    "verified_at": null,
    "activated_at": null,
    "ready_for_qurls": true,
    "dns_records": [
      {
        "type": "",
        "name": "",
        "value": "",
        "verified": true
      }
    ]
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### DomainListResponse

- **Type:**`object`

* **`data`**

  `array`

  **Items:**

  - **`acme_cname_target`**

    `string` — CNAME target for ACME certificate provisioning

  - **`activated_at`**

    `string | null`, format: `date-time`

  - **`created_at`**

    `string`, format: `date-time`

  - **`dns_records`**

    `array` — Required DNS records for domain setup

    **Items:**

    - **`name`**

      `string` — DNS record name

    - **`type`**

      `string` — DNS record type (TXT, CNAME)

    - **`value`**

      `string` — DNS record value

    - **`verified`**

      `boolean` — Whether this record has been verified

  - **`domain`**

    `string` — The custom domain name

  - **`ready_for_qurls`**

    `boolean` — Whether this domain is ready to be used with qURLs (status is verified, provisioning\_tls, or active)

  - **`status`**

    `string`, possible values: `"pending_verification", "verified", "provisioning_tls", "active", "failed"` — Current domain status

  - **`token_expires_at`**

    `string | null`, format: `date-time` — When the verification token expires. Omitted for domains created before token expiry was introduced (those tokens never expire). After expiry, use the regenerate-token endpoint to get a new one.

  - **`verification_token`**

    `string` — TXT record value for DNS verification

  - **`verified_at`**

    `string | null`, format: `date-time`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string`

  - **`has_more`**

    `boolean` — Whether more items exist

  - **`next_cursor`**

    `string` — Cursor for next page (empty if no more pages)

  - **`page_size`**

    `integer` — Number of items returned

**Example:**

```json
{
  "data": [
    {
      "domain": "",
      "status": "pending_verification",
      "verification_token": "",
      "token_expires_at": null,
      "acme_cname_target": "",
      "created_at": "",
      "verified_at": null,
      "activated_at": null,
      "ready_for_qurls": true,
      "dns_records": [
        {
          "type": "",
          "name": "",
          "value": "",
          "verified": true
        }
      ]
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### CheckDetail

- **Type:**`object`

* **`verified` (required)**

  `boolean`

* **`error`**

  `string` — Error message when the check fails (omitted when verified).

* **`found`**

  `string` — The actual value found in DNS (omitted when nothing was found).

**Example:**

```json
{
  "verified": true,
  "error": "",
  "found": ""
}
```

### DomainVerifyData

- **Type:**`object`

* **`checks`**

  `object`

  - **`acme_cname`**

    `object`

    - **`verified` (required)**

      `boolean`

    - **`error`**

      `string` — Error message when the check fails (omitted when verified).

    - **`found`**

      `string` — The actual value found in DNS (omitted when nothing was found).

  - **`traffic_routing`**

    `object`

    - **`verified` (required)**

      `boolean`

    - **`error`**

      `string` — Error message when the check fails (omitted when verified).

    - **`found`**

      `string` — The actual value found in DNS (omitted when nothing was found).

  - **`txt`**

    `object`

    - **`verified` (required)**

      `boolean`

    - **`error`**

      `string` — Error message when the check fails (omitted when verified).

    - **`found`**

      `string` — The actual value found in DNS (omitted when nothing was found).

* **`domain`**

  `string`

* **`status`**

  `string`, possible values: `"pending_verification", "verified", "provisioning_tls", "active", "failed"`

**Example:**

```json
{
  "domain": "",
  "status": "pending_verification",
  "checks": {
    "txt": {
      "verified": true,
      "error": "",
      "found": ""
    },
    "acme_cname": {
      "verified": true,
      "error": "",
      "found": ""
    },
    "traffic_routing": {
      "verified": true,
      "error": "",
      "found": ""
    }
  }
}
```

### DomainVerifyResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`checks`**

    `object`

    - **`acme_cname`**

      `object`

      - **`verified` (required)**

        `boolean`

      - **`error`**

        `string` — Error message when the check fails (omitted when verified).

      - **`found`**

        `string` — The actual value found in DNS (omitted when nothing was found).

    - **`traffic_routing`**

      `object`

      - **`verified` (required)**

        `boolean`

      - **`error`**

        `string` — Error message when the check fails (omitted when verified).

      - **`found`**

        `string` — The actual value found in DNS (omitted when nothing was found).

    - **`txt`**

      `object`

      - **`verified` (required)**

        `boolean`

      - **`error`**

        `string` — Error message when the check fails (omitted when verified).

      - **`found`**

        `string` — The actual value found in DNS (omitted when nothing was found).

  - **`domain`**

    `string`

  - **`status`**

    `string`, possible values: `"pending_verification", "verified", "provisioning_tls", "active", "failed"`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "data": {
    "domain": "",
    "status": "pending_verification",
    "checks": {
      "txt": {
        "verified": true,
        "error": "",
        "found": ""
      },
      "acme_cname": {
        "verified": true,
        "error": "",
        "found": ""
      },
      "traffic_routing": {
        "verified": true,
        "error": "",
        "found": ""
      }
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### Meta

- **Type:**`object`

* **`request_id` (required)**

  `string` — Unique request identifier for tracing

* **`found_existing`**

  `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

* **`tombstone`**

  `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

  - **`tombstoned_at` (required)**

    `string`, format: `date-time` — When the resource's lifecycle closed.

  - **`final_access_count`**

    `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "request_id": "",
  "found_existing": true,
  "tombstone": {
    "tombstoned_at": "2026-01-01T00:00:00Z",
    "final_access_count": 42
  }
}
```

### TombstoneInfo

- **Type:**`object`

Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on.

**Only present on 410 Gone error responses.** Although `Meta.tombstone` is structurally declared on every envelope's `Meta`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses.

Returned in the `meta.tombstone` field of 410 Gone responses on `GET /v1/resources/{id}`, `POST /v1/resources/{id}/qurls`, and `PATCH /v1/resources/{id}` once the resource's lifecycle has closed.

Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

- **`tombstoned_at` (required)**

  `string`, format: `date-time` — When the resource's lifecycle closed.

- **`final_access_count`**

  `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "tombstoned_at": "2026-01-01T00:00:00Z",
  "final_access_count": 42
}
```

### ListMeta

- **Type:**`object`

* **`request_id` (required)**

  `string`

* **`has_more`**

  `boolean` — Whether more items exist

* **`next_cursor`**

  `string` — Cursor for next page (empty if no more pages)

* **`page_size`**

  `integer` — Number of items returned

**Example:**

```json
{
  "request_id": "",
  "page_size": 1,
  "has_more": true,
  "next_cursor": ""
}
```

### Error

- **Type:**`object`

Error response following RFC 7807 Problem Details for HTTP APIs. See: <https://datatracker.ietf.org/doc/html/rfc7807>

- **`code` (required)**

  `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

- **`status` (required)**

  `integer` — HTTP status code for this occurrence (RFC 7807).

- **`title` (required)**

  `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

- **`type` (required)**

  `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

- **`detail`**

  `string` — Human-readable explanation specific to this occurrence (RFC 7807).

- **`instance`**

  `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

**Example:**

```json
{
  "type": "https://api.qurl.link/problems/invalid_request",
  "title": "Invalid Request",
  "status": 400,
  "detail": "The target_url field must be a valid HTTPS URL",
  "instance": "/v1/qurls",
  "code": "invalid_request"
}
```

### ValidationError

- **Type:**

**Example:**

### ErrorEnvelope

- **Type:**`object`

* **`error`**

  `object` — Error response following RFC 7807 Problem Details for HTTP APIs. See: https\://datatracker.ietf.org/doc/html/rfc7807

  - **`code` (required)**

    `string` — Machine-readable error code (extension field for backward compatibility). Maps to the problem type slug.

  - **`status` (required)**

    `integer` — HTTP status code for this occurrence (RFC 7807).

  - **`title` (required)**

    `string` — Short, human-readable summary of the problem type (RFC 7807). This should not change from occurrence to occurrence.

  - **`type` (required)**

    `string`, format: `uri` — URI reference that identifies the problem type (RFC 7807). When dereferenced, it should provide human-readable documentation.

  - **`detail`**

    `string` — Human-readable explanation specific to this occurrence (RFC 7807).

  - **`instance`**

    `string`, format: `uri-reference` — URI reference that identifies the specific occurrence (RFC 7807). Typically the request path.

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request"
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```

### ValidationErrorEnvelope

- **Type:**`object`

* **`error`**

  `object`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

  - **`found_existing`**

    `boolean` — Present only on 201 responses from \`POST /v1/resources\`. \`true\` means the returned \`resource\_id\` belonged to an already-active resource rather than one this call created. Both create paths report it. With \`find\_or\_create=true\`, it reflects reuse of an existing active qURL Connector resource; reuse of a slug after its previous resource was revoked returns a fresh \`resource\_id\` with \`false\`. On a plain URL create, it reflects the per-owner, per-target idempotency: publishing a \`target\_url\` that already has an active resource returns that resource and its existing CRID with \`true\`, while a target whose previous resource was revoked or retired gets a brand-new resource with \`false\`.

  - **`tombstone`**

    `object` — Read-only metadata for a tombstoned resource. Field-level documentation below spells out which resource types each field surfaces on. \*\*Only present on 410 Gone error responses.\*\* Although \`Meta.tombstone\` is structurally declared on every envelope's \`Meta\`, it is always absent from non-410 responses — do not branch your client on its presence for non-error responses. Returned in the \`meta.tombstone\` field of 410 Gone responses on \`GET /v1/resources/{id}\`, \`POST /v1/resources/{id}/qurls\`, and \`PATCH /v1/resources/{id}\` once the resource's lifecycle has closed. Tombstoned resources are gated against re-mint and will be hard-deleted by the API after a retention window (default 90 days). Until hard-delete, this metadata remains queryable as the read-only record of when the lifecycle closed.

    - **`tombstoned_at` (required)**

      `string`, format: `date-time` — When the resource's lifecycle closed.

    - **`final_access_count`**

      `integer`, format: `int64` — Snapshot of total accesses across all qURLs for this resource at tombstone time. \*\*Present only on URL and qURL Connector resources\*\* (\`type=url\` and \`type=tunnel\` on the wire). For connector-owned (\`type=transit\`) rows the count is stripped per the redaction contract, since the connector installer is not necessarily the uploader and learning "was this viewed" exposes sensitive viewer-fact-of-presence about uploads the installer didn't make. When present, value is >= 1. Absent on URL / qURL Connector rows means either no accesses occurred before closure OR the snapshot was not taken — these two cases are indistinguishable on the wire by design, since both collapse to the same "absent" representation.

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/invalid_request",
    "title": "Invalid Request",
    "status": 400,
    "detail": "The target_url field must be a valid HTTPS URL",
    "instance": "/v1/qurls",
    "code": "invalid_request",
    "invalid_fields": {
      "target_url": "must be a valid HTTPS URL",
      "description": "must not exceed 500 characters"
    }
  },
  "meta": {
    "request_id": "",
    "found_existing": true,
    "tombstone": {
      "tombstoned_at": "2026-01-01T00:00:00Z",
      "final_access_count": 42
    }
  }
}
```
