# QURL Service API

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

API for managing QURL resources - secure, time-limited access links to protected resources.

## Authentication

The public API requires Auth0 JWT tokens or API keys with appropriate scopes:

- `qurl:read` - Read access to QURLs and quota
- `qurl:write` - Create, update, and delete QURLs
- `qurl:resolve` - Resolve access tokens and trigger firewall access (headless)
- `qurl:write` also covers webhook management

## Rate Limiting

- Per-owner: Configurable limit (default varies by plan)
- Per-IP: Configurable limit for internal endpoints

Rate limit exceeded responses include a `Retry-After` header.

## Idempotency

Mutating endpoints (POST, PUT, PATCH) support idempotency via the `Idempotency-Key` header. When provided, the response is cached and subsequent requests with the same key return the cached response with `Idempotency-Replayed: true` header.

## Response Envelope

All responses use a consistent envelope format:

```json
{
  "data": { ... },      // For success responses
  "error": { ... },     // For error responses (RFC 7807 format)
  "meta": {
    "request_id": "...",
    "page_size": 20,    // For list responses
    "has_more": true,   // For list responses
    "next_cursor": "..."// For list responses
  }
}
```

## Error Responses (RFC 7807)

Error responses follow [RFC 7807 Problem Details](https://datatracker.ietf.org/doc/html/rfc7807) with `Content-Type: application/problem+json`:

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

For validation errors, an additional `invalid_fields` map is included.

## Caching

GET endpoints support ETag-based caching. Include `If-None-Match` header with the ETag value to receive a 304 Not Modified response when content hasn't changed.

## Object Model

- **Resource** (`r_` prefix): Long-lived container grouped by target URL. Holds metadata (tags, description, custom domain). Auto-created on first QURL for a target URL.
- **QURL / Access Token** (`q_` display ID, `at_` token): Short-lived, policy-bound access token. Belongs to one Resource. Owns expiry, access policy, session limits.
- **Access Link**: The `qurl.link/at_xxx` URL returned once at creation time.

The `/v1/qurls` endpoints are resource-centric — `{id}` accepts both resource IDs (`r_`) and QURL display IDs (`q_`). Per-QURL management uses `DELETE /v1/resources/{id}/qurls/{qurl_id}` to revoke individual tokens.

## Webhooks

Subscribe to events via webhook endpoints. Webhook payloads are signed with HMAC-SHA256 using your webhook secret. Verify the `QURL-Signature` header.

## Servers

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

## Operations

### Resolve a QURL access token (headless)

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

Resolves a QURL access token and triggers an NHP knock to open firewall access for the caller's IP address. Returns the target URL that the caller can access directly for the duration of the access grant.

This endpoint is designed for AI agents and programmatic clients that cannot follow the browser-based QURL flow.

**Important:** The firewall is opened for the IP address making this request. The `src_ip` in the response reflects which IP was granted access.

**One-time tokens:** If the token is one-time-use and the NHP knock fails after token resolution, the token is consumed but no access is granted. 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 firewall access granted

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

- **`data`**

  `object`

  - **`access_grant`**

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

    - **`expires_in`**

      `integer` — Seconds until firewall access expires

    - **`granted_at`**

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

    - **`src_ip`**

      `string` — The IP address that was granted access

  - **`resource_id`**

    `string` — QURL resource identifier

  - **`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

**Example:**

```json
{
  "data": {
    "target_url": "https://api.example.com/data",
    "resource_id": "r_k8xqp9h2sj9",
    "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": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### Status: 410 Resource is no longer available (consumed, expired, or revoked)

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

- **`error`**

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

  - **`code` (required)**

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

  - **`status` (required)**

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

  - **`title` (required)**

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

  - **`type` (required)**

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

  - **`detail`**

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

  - **`instance`**

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

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### Status: 502 Upstream NHP knock failed

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

- **`error`**

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

  - **`code` (required)**

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

  - **`status` (required)**

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

  - **`title` (required)**

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

  - **`type` (required)**

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

  - **`detail`**

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

  - **`instance`**

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

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

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

### Create a new QURL

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

Creates a QURL access token for a target URL. Auto-creates a resource if none exists for the target URL.

#### Request Body

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

- **`target_url` (required)**

  `string`, format: `uri` — The URL to protect with QURL (max 2048 characters)

- **`access_policy`**

  `object` — Access control policy for the QURL

  - **`ai_agent_policy`**

    `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

    - **`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` — Duration until expiration. Supports: hours (24h), days (7d), weeks (1w). Minimum: 1 minute. Maximum varies by plan: free=3 days, growth/enterprise=30 days. Default: 24h.

- **`label`**

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

- **`max_sessions`**

  `integer` — Maximum concurrent sessions allowed. 0 = unlimited (default), 1-1000 = hard limit.

- **`one_time_use`**

  `boolean`, default: `false` — Whether this QURL expires after a single use

- **`session_duration`**

  `string` — How long access lasts after someone clicks this QURL. Controls the session/cookie lifetime, separate from the QURL link expiration (expires\_in). Supports: minutes (5m), hours (1h), days (1d). Minimum: 5 minutes. Maximum: 24 hours. Default: server default (typically 1 hour).

**Example:**

```json
{
  "target_url": "https://internal.example.com/dashboard",
  "expires_in": "7d",
  "one_time_use": false,
  "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

  - **`expires_at`**

    `string`, format: `date-time`

  - **`label`**

    `string` — Human-readable label for this QURL

  - **`qurl_id`**

    `string` — Unique QURL identifier (q\_ + first 11 hex chars of token hash). Used as NHP resource ID and qurl\_site subdomain. Each QURL gets its own firewall rules keyed by this ID.

  - **`qurl_link`**

    `string`, format: `uri` — Ephemeral access link (shown once, cannot be recovered)

  - **`qurl_site`**

    `string`, format: `uri` — Per-QURL site URL (https\://{qurl\_id}.qurl.site)

  - **`resource_id`**

    `string` — Parent resource ID (auto-created grouping by target URL)

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "qurl_id": "q_3a7f2c8e91b",
    "resource_id": "",
    "qurl_link": "",
    "qurl_site": "",
    "expires_at": "",
    "label": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### List QURLs

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

Lists resources owned by the authenticated user. Each resource groups QURLs sharing the same target URL. Supports filtering by status and date ranges, with cursor-based pagination.

#### Responses

##### Status: 200 List of QURLs

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

- **`data`**

  `array`

  **Items:**

  - **`created_at`**

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

  - **`custom_domain`**

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

  - **`description`**

    `string` — Human-readable description

  - **`expires_at`**

    `string`, format: `date-time` — When the QURL expires

  - **`qurl_count`**

    `integer` — Number of active QURLs (access tokens) for this resource

  - **`qurl_site`**

    `string`, format: `uri` — The QURL site URL for accessing this resource

  - **`qurls`**

    `array` — Per-QURL details. Present on single-get, omitted on list (too expensive).

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the QURL

      - **`ai_agent_policy`**

        `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

        - **`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`

    - **`session_duration`**

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

    - **`status`**

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

    - **`use_count`**

      `integer`

  - **`resource_id`**

    `string` — Unique resource identifier (generated by server)

  - **`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

- **`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": "",
      "target_url": "",
      "status": "active",
      "created_at": "",
      "expires_at": "",
      "description": "",
      "tags": [
        ""
      ],
      "qurl_site": "",
      "custom_domain": null,
      "qurl_count": 1,
      "qurls": [
        {
          "qurl_id": "",
          "label": "",
          "status": "active",
          "one_time_use": true,
          "max_sessions": 1,
          "session_duration": 1,
          "use_count": 1,
          "qurl_site": "",
          "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: 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

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Get a QURL

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

Retrieves a resource and its QURL tokens by resource or QURL display ID.

#### Responses

##### Status: 200 QURL details

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

- **`data`**

  `object` — Resource container for a protected URL. The \`qurls\` array (present on single-get responses) contains 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 QURL was created

  - **`custom_domain`**

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

  - **`description`**

    `string` — Human-readable description

  - **`expires_at`**

    `string`, format: `date-time` — When the QURL expires

  - **`qurl_count`**

    `integer` — Number of active QURLs (access tokens) for this resource

  - **`qurl_site`**

    `string`, format: `uri` — The QURL site URL for accessing this resource

  - **`qurls`**

    `array` — Per-QURL details. Present on single-get, omitted on list (too expensive).

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the QURL

      - **`ai_agent_policy`**

        `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

        - **`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`

    - **`session_duration`**

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

    - **`status`**

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

    - **`use_count`**

      `integer`

  - **`resource_id`**

    `string` — Unique resource identifier (generated by server)

  - **`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

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "resource_id": "",
    "target_url": "",
    "status": "active",
    "created_at": "",
    "expires_at": "",
    "description": "",
    "tags": [
      ""
    ],
    "qurl_site": "",
    "custom_domain": null,
    "qurl_count": 1,
    "qurls": [
      {
        "qurl_id": "",
        "label": "",
        "status": "active",
        "one_time_use": true,
        "max_sessions": 1,
        "session_duration": 1,
        "use_count": 1,
        "qurl_site": "",
        "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": ""
  }
}
```

##### 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

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Delete a QURL

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

Revokes a resource and all its QURLs. Requires a resource ID (r\_ prefix). To revoke a single token, use DELETE /v1/resources/{resource\_id}/qurls/{qurl\_id}.

#### Responses

##### Status: 204 QURL 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

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Update a QURL

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

Updates resource-level metadata (description, tags, expiration).

#### 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": "7d",
  "expires_at": "",
  "tags": [
    ""
  ],
  "description": ""
}
```

#### Responses

##### Status: 200 QURL updated successfully

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

- **`data`**

  `object` — Resource container for a protected URL. The \`qurls\` array (present on single-get responses) contains 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 QURL was created

  - **`custom_domain`**

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

  - **`description`**

    `string` — Human-readable description

  - **`expires_at`**

    `string`, format: `date-time` — When the QURL expires

  - **`qurl_count`**

    `integer` — Number of active QURLs (access tokens) for this resource

  - **`qurl_site`**

    `string`, format: `uri` — The QURL site URL for accessing this resource

  - **`qurls`**

    `array` — Per-QURL details. Present on single-get, omitted on list (too expensive).

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the QURL

      - **`ai_agent_policy`**

        `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

        - **`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`

    - **`session_duration`**

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

    - **`status`**

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

    - **`use_count`**

      `integer`

  - **`resource_id`**

    `string` — Unique resource identifier (generated by server)

  - **`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

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "resource_id": "",
    "target_url": "",
    "status": "active",
    "created_at": "",
    "expires_at": "",
    "description": "",
    "tags": [
      ""
    ],
    "qurl_site": "",
    "custom_domain": null,
    "qurl_count": 1,
    "qurls": [
      {
        "qurl_id": "",
        "label": "",
        "status": "active",
        "one_time_use": true,
        "max_sessions": 1,
        "session_duration": 1,
        "use_count": 1,
        "qurl_site": "",
        "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": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Mint an access link

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

Creates a new QURL access token for an existing resource. The link can be shared with users who need temporary access to the resource.

#### Request Body

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

- **`access_policy`**

  `object` — Access control policy for the QURL

  - **`ai_agent_policy`**

    `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

    - **`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 (0 = unlimited)

- **`one_time_use`**

  `boolean`, default: `false` — Whether this QURL can only be used once

- **`session_duration`**

  `string` — How long access lasts after clicking. Minimum: 5 minutes. Maximum: 24 hours.

**Example:**

```json
{
  "expires_in": "5m",
  "expires_at": "",
  "label": "Alice from Acme",
  "one_time_use": false,
  "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"
      ]
    }
  }
}
```

#### Responses

##### Status: 201 Access link created

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

- **`data`**

  `object`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`qurl_link`**

    `string`, format: `uri` — Single-use access link

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "qurl_link": "",
    "expires_at": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Batch create QURLs

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

Creates multiple QURL resources in a single request. Returns 201 if all succeed, 207 Multi-Status if mixed results, or 400 if all fail.

**Validation is atomic:** if any item fails input validation (e.g. invalid tags, description too long), the entire batch is rejected with per-item error details. Items that passed validation are marked as "skipped" in the results.

#### Request Body

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

- **`items` (required)**

  `array` — Array of QURL creation requests (1-100 items)

  **Items:**

  - **`target_url` (required)**

    `string`, format: `uri` — The URL to protect with QURL (max 2048 characters)

  - **`access_policy`**

    `object` — Access control policy for the QURL

    - **`ai_agent_policy`**

      `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

      - **`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` — Duration until expiration. Supports: hours (24h), days (7d), weeks (1w). Minimum: 1 minute. Maximum varies by plan: free=3 days, growth/enterprise=30 days. Default: 24h.

  - **`label`**

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

  - **`max_sessions`**

    `integer` — Maximum concurrent sessions allowed. 0 = unlimited (default), 1-1000 = hard limit.

  - **`one_time_use`**

    `boolean`, default: `false` — Whether this QURL expires after a single use

  - **`session_duration`**

    `string` — How long access lasts after someone clicks this QURL. Controls the session/cookie lifetime, separate from the QURL link expiration (expires\_in). Supports: minutes (5m), hours (1h), days (1d). Minimum: 5 minutes. Maximum: 24 hours. Default: server default (typically 1 hour).

**Example:**

```json
{
  "items": [
    {
      "target_url": "https://internal.example.com/dashboard",
      "expires_in": "7d",
      "one_time_use": false,
      "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:**

    - **`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` — QURL site URL (if success)

    - **`resource_id`**

      `string` — Created resource ID (if success)

    - **`success`**

      `boolean`

  - **`succeeded`**

    `integer` — Number of successfully created QURLs

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "succeeded": 1,
    "failed": 1,
    "results": [
      {
        "index": 1,
        "success": true,
        "resource_id": "",
        "qurl_link": "",
        "qurl_site": "",
        "expires_at": "",
        "error": {
          "code": "",
          "message": ""
        }
      }
    ]
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 207 Mixed results (some succeeded, some failed)

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

- **`data`**

  `object`

  - **`failed`**

    `integer` — Number of failed creations

  - **`results`**

    `array`

    **Items:**

    - **`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` — QURL site URL (if success)

    - **`resource_id`**

      `string` — Created resource ID (if success)

    - **`success`**

      `boolean`

  - **`succeeded`**

    `integer` — Number of successfully created QURLs

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "succeeded": 1,
    "failed": 1,
    "results": [
      {
        "index": 1,
        "success": true,
        "resource_id": "",
        "qurl_link": "",
        "qurl_site": "",
        "expires_at": "",
        "error": {
          "code": "",
          "message": ""
        }
      }
    ]
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 400 All items failed or invalid request

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

- **`data`**

  `object`

  - **`failed`**

    `integer` — Number of failed creations

  - **`results`**

    `array`

    **Items:**

    - **`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` — QURL site URL (if success)

    - **`resource_id`**

      `string` — Created resource ID (if success)

    - **`success`**

      `boolean`

  - **`succeeded`**

    `integer` — Number of successfully created QURLs

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "succeeded": 1,
    "failed": 1,
    "results": [
      {
        "index": 1,
        "success": true,
        "resource_id": "",
        "qurl_link": "",
        "qurl_site": "",
        "expires_at": "",
        "error": {
          "code": "",
          "message": ""
        }
      }
    ]
  },
  "meta": {
    "request_id": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### List resources

- **Method:** `GET`
- **Path:** `/v1/resources`
- **Tags:** Resources

Lists all resources (target URL groupings) for the authenticated user. Each resource groups QURLs that share the same target URL. The response includes a `qurl_count` for each resource showing how many active QURLs exist for it. Use GET /v1/resources/{id} to see the individual QURLs.

#### Responses

##### Status: 200 Resource list

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

- **`data`**

  `array`

  **Items:**

  - **`created_at`**

    `string`, format: `date-time`

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`qurl_count`**

    `integer` — Number of active QURLs for this resource

  - **`resource_id`**

    `string`

  - **`status`**

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

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `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": [
    {
      "resource_id": "",
      "target_url": "",
      "status": "active",
      "description": "",
      "tags": [
        ""
      ],
      "custom_domain": null,
      "preserve_host": false,
      "qurl_count": 1,
      "created_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

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

### Create resource

- **Method:** `POST`
- **Path:** `/v1/resources`
- **Tags:** Resources

Explicitly create a resource (register a target URL before creating QURLs for it). If a resource already exists for the same target URL and owner, the existing resource is returned (idempotent).

#### Request Body

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

- **`target_url` (required)**

  `string`, format: `uri` — The URL to protect

- **`custom_domain`**

  `string`

- **`description`**

  `string`

- **`tags`**

  `array`

  **Items:**

  `string`

**Example:**

```json
{
  "target_url": "",
  "description": "",
  "tags": [
    ""
  ],
  "custom_domain": ""
}
```

#### Responses

##### Status: 201 Resource created

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

- **`data`**

  `object`

  - **`created_at`**

    `string`, format: `date-time`

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`qurl_count`**

    `integer` — Number of active QURLs for this resource

  - **`resource_id`**

    `string`

  - **`status`**

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

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `string`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "resource_id": "",
    "target_url": "",
    "status": "active",
    "description": "",
    "tags": [
      ""
    ],
    "custom_domain": null,
    "preserve_host": false,
    "qurl_count": 1,
    "created_at": "",
    "expires_at": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

### PARAMETERS /v1/resources/{id}

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

### Get resource details

- **Method:** `GET`
- **Path:** `/v1/resources/{id}`
- **Tags:** Resources

Get resource details including all QURLs associated with it. Each QURL has its own label, expiry, access policy, and NHP firewall rules — independent of other QURLs on the same resource.

#### Responses

##### Status: 200 Resource details

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

- **`data`**

  `object`

  - **`qurls`**

    `array`

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the QURL

      - **`ai_agent_policy`**

        `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

        - **`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`

    - **`session_duration`**

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

    - **`status`**

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

    - **`use_count`**

      `integer`

  - **`resource`**

    `object`

    - **`created_at`**

      `string`, format: `date-time`

    - **`custom_domain`**

      `string | null`

    - **`description`**

      `string`

    - **`expires_at`**

      `string`, format: `date-time`

    - **`preserve_host`**

      `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

    - **`qurl_count`**

      `integer` — Number of active QURLs for this resource

    - **`resource_id`**

      `string`

    - **`status`**

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

    - **`tags`**

      `array`

      **Items:**

      `string`

    - **`target_url`**

      `string`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "resource": {
      "resource_id": "",
      "target_url": "",
      "status": "active",
      "description": "",
      "tags": [
        ""
      ],
      "custom_domain": null,
      "preserve_host": false,
      "qurl_count": 1,
      "created_at": "",
      "expires_at": ""
    },
    "qurls": [
      {
        "qurl_id": "",
        "label": "",
        "status": "active",
        "one_time_use": true,
        "max_sessions": 1,
        "session_duration": 1,
        "use_count": 1,
        "qurl_site": "",
        "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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Update resource metadata

- **Method:** `PATCH`
- **Path:** `/v1/resources/{id}`
- **Tags:** Resources

Update resource metadata (tags, description, custom\_domain).

#### Request Body

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

- **`custom_domain`**

  `string`

- **`description`**

  `string`

- **`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
{
  "description": "",
  "tags": [
    ""
  ],
  "custom_domain": "",
  "preserve_host": true
}
```

#### Responses

##### Status: 200 Resource updated

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

- **`data`**

  `object`

  - **`created_at`**

    `string`, format: `date-time`

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`qurl_count`**

    `integer` — Number of active QURLs for this resource

  - **`resource_id`**

    `string`

  - **`status`**

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

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `string`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "resource_id": "",
    "target_url": "",
    "status": "active",
    "description": "",
    "tags": [
      ""
    ],
    "custom_domain": null,
    "preserve_host": false,
    "qurl_count": 1,
    "created_at": "",
    "expires_at": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Revoke resource and all its QURLs

- **Method:** `DELETE`
- **Path:** `/v1/resources/{id}`
- **Tags:** Resources

Revokes a resource. All QURLs associated with the resource become inaccessible because token resolution checks the resource status.

#### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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. The QURL must be active. Other QURLs on the same resource are not affected.

#### 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

**Example:**

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

##### 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

**Example:**

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

##### Status: 409 QURL is not active

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

- **`error`**

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

  - **`code` (required)**

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

  - **`status` (required)**

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

  - **`title` (required)**

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

  - **`type` (required)**

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

  - **`detail`**

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

  - **`instance`**

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

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

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

##### 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

**Example:**

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

### Update a specific QURL

- **Method:** `PATCH`
- **Path:** `/v1/resources/{id}/qurls/{qurl_id}`
- **Tags:** Resources

Update expiry, label, access policy, or max\_sessions on a specific QURL token. The QURL must be active. At least one field must be provided.

#### Request Body

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

- **`access_policy`**

  `object` — Access control policy for the QURL

  - **`ai_agent_policy`**

    `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

    - **`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` — Maximum concurrent sessions (0 = unlimited).

- **`session_duration`**

  `string` — How long access lasts after clicking. Minimum: 5 minutes. Maximum: 24 hours.

**Example:**

```json
{
  "extend_by": "7d",
  "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

    - **`ai_agent_policy`**

      `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

      - **`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`

  - **`session_duration`**

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

  - **`status`**

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

  - **`use_count`**

    `integer`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "qurl_id": "",
    "label": "",
    "status": "active",
    "one_time_use": true,
    "max_sessions": 1,
    "session_duration": 1,
    "use_count": 1,
    "qurl_site": "",
    "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": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### Status: 409 QURL is not active

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

- **`error`**

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

  - **`code` (required)**

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

  - **`status` (required)**

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

  - **`title` (required)**

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

  - **`type` (required)**

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

  - **`detail`**

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

  - **`instance`**

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

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

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

##### 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

**Example:**

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

### List active sessions for a resource

- **Method:** `GET`
- **Path:** `/v1/resources/{id}/sessions`
- **Tags:** Sessions

Returns all active access sessions for the specified resource.

#### 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` — Unique request identifier for tracing

**Example:**

```json
{
  "data": [
    {
      "session_id": "",
      "qurl_id": "",
      "src_ip": "",
      "user_agent": "",
      "created_at": "",
      "last_seen_at": ""
    }
  ],
  "meta": {
    "request_id": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Terminate all active sessions for a resource

- **Method:** `DELETE`
- **Path:** `/v1/resources/{id}/sessions`
- **Tags:** Sessions

Immediately terminates all active sessions for the specified resource.

#### Responses

##### Status: 200 Sessions terminated

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

- **`data`**

  `object`

  - **`terminated`**

    `integer` — Number of sessions terminated

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "terminated": 1
  },
  "meta": {
    "request_id": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Terminate a specific session

- **Method:** `DELETE`
- **Path:** `/v1/resources/{id}/sessions/{session_id}`
- **Tags:** Sessions

Immediately terminates a specific active session.

#### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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` — Maximum active QURLs allowed (-1 = unlimited)

    - **`max_expiry_seconds`**

      `integer` — Maximum expiry duration in seconds (free=259200/3d, growth=2592000/30d, enterprise=2592000/30d)

    - **`max_tokens_per_qurl`**

      `integer` — Maximum tokens per QURL (-1 = unlimited)

    - **`resolve_per_minute`**

      `integer` — Rate limit for internal token resolution (used by access infrastructure)

  - **`usage`**

    `object`

    - **`active_qurls`**

      `integer` — Currently active QURLs

    - **`active_qurls_percent`**

      `number | null`, format: `float` — Percentage of max\_active\_qurls used (0-100, null if unlimited)

    - **`qurls_created`**

      `integer` — Total QURLs created this period

    - **`total_accesses`**

      `integer` — Total access attempts this period

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**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_active_qurls": 1,
      "max_tokens_per_qurl": 1,
      "max_expiry_seconds": 1
    },
    "usage": {
      "qurls_created": 1,
      "active_qurls": 1,
      "active_qurls_percent": null,
      "total_accesses": 1
    }
  },
  "meta": {
    "request_id": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Get current billing period usage

- **Method:** `GET`
- **Path:** `/v1/usage/current-period`
- **Tags:** Usage

Returns usage statistics for the current billing period, including the number of QURLs created, the user's tier, period dates, and a cost estimate for usage-based plans (Growth tier). Requires JWT authentication (dashboard endpoint).

#### Responses

##### Status: 200 Current period usage information

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

- **`data`**

  `object`

  - **`active_qurls` (required)**

    `integer` — Number of currently active (non-expired, non-revoked) QURLs

  - **`period_end` (required)**

    `string`, format: `date-time` — End of the current billing period (UTC)

  - **`period_start` (required)**

    `string`, format: `date-time` — Start of the current billing period (UTC)

  - **`qurls_created` (required)**

    `integer` — Number of QURLs created in this period

  - **`tier` (required)**

    `string`, possible values: `"free", "growth", "enterprise"` — Current billing tier

  - **`cost_estimate`**

    `object`

    - **`amount_cents` (required)**

      `integer` — 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

**Example:**

```json
{
  "data": {
    "tier": "free",
    "period_start": "",
    "period_end": "",
    "qurls_created": 1,
    "active_qurls": 1,
    "cost_estimate": {
      "currency": "usd",
      "amount_cents": 1500,
      "description": "150 QURLs x $0.10/QURL"
    }
  },
  "meta": {
    "request_id": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Get daily usage breakdown

- **Method:** `GET`
- **Path:** `/v1/usage/daily`
- **Tags:** Usage

Returns a daily breakdown of QURL creation counts for the current billing period. Intended for dashboard chart rendering. Requires JWT authentication (dashboard endpoint).

#### Responses

##### Status: 200 Daily usage breakdown

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

- **`data`**

  `object`

  - **`daily` (required)**

    `array` — One entry per day from period\_start to today

    **Items:**

    - **`date` (required)**

      `string`, format: `date` — Calendar date (YYYY-MM-DD)

    - **`qurls_created` (required)**

      `integer` — QURLs created on this date

  - **`period_end` (required)**

    `string`, format: `date-time` — End of the current billing period (UTC)

  - **`period_start` (required)**

    `string`, format: `date-time` — Start of the current billing period (UTC)

  - **`tier` (required)**

    `string`, possible values: `"free", "growth", "enterprise"` — Current billing tier

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "tier": "free",
    "period_start": "",
    "period_end": "",
    "daily": [
      {
        "date": "",
        "qurls_created": 1
      }
    ]
  },
  "meta": {
    "request_id": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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-tier record on first access (lazy provisioning). Requires JWT authentication (dashboard endpoint). 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

  - **`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

**Example:**

```json
{
  "data": {
    "tier": "free",
    "spending_cap_cents": 1,
    "current_period_usage": 1,
    "frozen": true,
    "frozen_reason": "spending_cap"
  },
  "meta": {
    "request_id": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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

  - **`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

**Example:**

```json
{
  "data": {
    "tier": "free",
    "spending_cap_cents": 1,
    "current_period_usage": 1,
    "frozen": true,
    "frozen_reason": "spending_cap"
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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 JWT authentication (dashboard endpoint). 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.

#### 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 at the OpenAPI middleware layer as an invalid enum value before the handler runs.

**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

**Example:**

```json
{
  "data": {
    "url": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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 JWT authentication (dashboard endpoint). 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

**Example:**

```json
{
  "data": {
    "url": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### List invoices

- **Method:** `GET`
- **Path:** `/v1/billing/invoices`
- **Tags:** Billing

Returns the authenticated user's Stripe invoices. Requires JWT authentication (dashboard endpoint). 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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

**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": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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

**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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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

**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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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

**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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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

  - **`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": "",
      "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

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "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": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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

**Example:**

```json
{
  "data": [
    {
      "type": "qurl.created",
      "category": "resource",
      "description": ""
    }
  ],
  "meta": {
    "request_id": ""
  }
}
```

### 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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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

  - **`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

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "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": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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

  - **`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

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "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": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "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": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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`

  - **`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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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` — Truncated response body (max 8KB)

  - **`response_code`**

    `integer`

  - **`retry_count`**

    `integer`

  - **`status`**

    `string`, possible values: `"pending", "success", "failed", "retrying", "abandoned"`

  - **`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

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Create a new API key

- **Method:** `POST`
- **Path:** `/v1/api-keys`
- **Tags:** API Keys

Creates a new API key for the authenticated user. The plaintext API key is returned only once in the response - store it securely. Requires JWT authentication (API keys cannot create API keys).

#### Request Body

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

- **`name` (required)**

  `string` — Human-readable name for the API key

- **`scopes` (required)**

  `array` — Permissions granted to this API key

  **Items:**

  `string`, possible values: `"qurl:read", "qurl:write", "qurl:resolve"`

**Example:**

```json
{
  "name": "",
  "scopes": [
    "qurl:read"
  ]
}
```

#### Responses

##### Status: 201 API key created successfully

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

- **`data`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "key_id": "",
    "key_prefix": "",
    "name": "",
    "scopes": [
      ""
    ],
    "status": "active",
    "created_at": "",
    "updated_at": "",
    "last_used_at": "",
    "api_key": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### Status: 403 Forbidden. Either: - API key auth was used (JWT required for key management), 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### List API keys

- **Method:** `GET`
- **Path:** `/v1/api-keys`
- **Tags:** API Keys

Lists API keys owned by the authenticated user. The plaintext API key is never returned in list responses. Requires JWT authentication (API keys cannot list API keys).

#### Responses

##### Status: 200 List of API keys

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

- **`data`**

  `array`

  **Items:**

  - **`created_at`**

    `string`, format: `date-time`

  - **`key_id`**

    `string`

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

  - **`last_used_at`**

    `string`, format: `date-time`

  - **`name`**

    `string`

  - **`scopes`**

    `array` — Permissions granted to this API key. Returned in alphabetical order for deterministic responses.

    **Items:**

    `string`

  - **`status`**

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

  - **`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": "",
      "name": "",
      "scopes": [
        ""
      ],
      "status": "active",
      "created_at": "",
      "updated_at": "",
      "last_used_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

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Update an API key

- **Method:** `PATCH`
- **Path:** `/v1/api-keys/{key_id}`
- **Tags:** API Keys

Updates the name or scopes of an existing API key. Requires JWT authentication (API keys cannot update API keys). If neither name nor scopes are provided, the existing key is returned unchanged (no-op).

#### Request Body

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

- **`name`**

  `string` — Updated name for the API key

- **`scopes`**

  `array` — Updated permissions for the API 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"`

**Example:**

```json
{
  "name": "",
  "scopes": [
    "qurl:read"
  ]
}
```

#### Responses

##### Status: 200 API key updated successfully

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

- **`data`**

  `object`

  - **`created_at`**

    `string`, format: `date-time`

  - **`key_id`**

    `string`

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

  - **`last_used_at`**

    `string`, format: `date-time`

  - **`name`**

    `string`

  - **`scopes`**

    `array` — Permissions granted to this API key. Returned in alphabetical order for deterministic responses.

    **Items:**

    `string`

  - **`status`**

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

  - **`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

**Example:**

```json
{
  "data": {
    "key_id": "",
    "key_prefix": "",
    "name": "",
    "scopes": [
      ""
    ],
    "status": "active",
    "created_at": "",
    "updated_at": "",
    "last_used_at": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### Revoke an API key

- **Method:** `DELETE`
- **Path:** `/v1/api-keys/{key_id}`
- **Tags:** API Keys

Revokes an API key (soft delete). The key status is set to "revoked" and a TTL is set for automatic cleanup after 30 days. Requires JWT authentication (API keys cannot revoke API keys).

#### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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.

#### 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

**Example:**

```json
{
  "data": {
    "redirect_url": "https://qurl.link/#at_k8xqp9h2sj9lx7r4abcdef"
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

### Create an access code

- **Method:** `POST`
- **Path:** `/v1/access-codes`
- **Tags:** Access Codes

Creates a new access code for a QURL resource. 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` — ID of the QURL resource this code grants access to

- **`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": "r_k8xqp9h2sj9",
  "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

**Example:**

```json
{
  "data": {
    "access_code_id": "acd_k8xqp9h2sj9",
    "resource_id": "r_k8xqp9h2sj9",
    "name": "Investor Stats Q1",
    "status": "active",
    "max_uses": 10,
    "use_count": 3,
    "created_at": "",
    "expires_at": null,
    "code": "ac_k8xqp9h2sj9lx7r4abcdef"
  },
  "meta": {
    "request_id": ""
  }
}
```

##### Status: 400 Invalid request parameters

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

- **`error`**

  `object`

- **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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` — ID of the QURL 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

**Example:**

```json
{
  "data": [
    {
      "access_code_id": "acd_k8xqp9h2sj9",
      "resource_id": "r_k8xqp9h2sj9",
      "name": "Investor Stats Q1",
      "status": "active",
      "max_uses": 10,
      "use_count": 3,
      "created_at": "",
      "expires_at": null
    }
  ],
  "meta": {
    "request_id": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

### 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

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

##### 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

**Example:**

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

## Schemas

### CreateQurlRequest

- **Type:**`object`

* **`target_url` (required)**

  `string`, format: `uri` — The URL to protect with QURL (max 2048 characters)

* **`access_policy`**

  `object` — Access control policy for the QURL

  - **`ai_agent_policy`**

    `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

    - **`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` — Duration until expiration. Supports: hours (24h), days (7d), weeks (1w). Minimum: 1 minute. Maximum varies by plan: free=3 days, growth/enterprise=30 days. Default: 24h.

* **`label`**

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

* **`max_sessions`**

  `integer` — Maximum concurrent sessions allowed. 0 = unlimited (default), 1-1000 = hard limit.

* **`one_time_use`**

  `boolean`, default: `false` — Whether this QURL expires after a single use

* **`session_duration`**

  `string` — How long access lasts after someone clicks this QURL. Controls the session/cookie lifetime, separate from the QURL link expiration (expires\_in). Supports: minutes (5m), hours (1h), days (1d). Minimum: 5 minutes. Maximum: 24 hours. Default: server default (typically 1 hour).

**Example:**

```json
{
  "target_url": "https://internal.example.com/dashboard",
  "expires_in": "7d",
  "one_time_use": false,
  "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"
}
```

### CreateResourceRequest

- **Type:**`object`

* **`target_url` (required)**

  `string`, format: `uri` — The URL to protect

* **`custom_domain`**

  `string`

* **`description`**

  `string`

* **`tags`**

  `array`

  **Items:**

  `string`

**Example:**

```json
{
  "target_url": "",
  "description": "",
  "tags": [
    ""
  ],
  "custom_domain": ""
}
```

### UpdateResourceRequest

- **Type:**`object`

* **`custom_domain`**

  `string`

* **`description`**

  `string`

* **`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
{
  "description": "",
  "tags": [
    ""
  ],
  "custom_domain": "",
  "preserve_host": true
}
```

### ResourceData

- **Type:**`object`

* **`created_at`**

  `string`, format: `date-time`

* **`custom_domain`**

  `string | null`

* **`description`**

  `string`

* **`expires_at`**

  `string`, format: `date-time`

* **`preserve_host`**

  `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

* **`qurl_count`**

  `integer` — Number of active QURLs for this resource

* **`resource_id`**

  `string`

* **`status`**

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

* **`tags`**

  `array`

  **Items:**

  `string`

* **`target_url`**

  `string`

**Example:**

```json
{
  "resource_id": "",
  "target_url": "",
  "status": "active",
  "description": "",
  "tags": [
    ""
  ],
  "custom_domain": null,
  "preserve_host": false,
  "qurl_count": 1,
  "created_at": "",
  "expires_at": ""
}
```

### ResourceDetailData

- **Type:**`object`

* **`qurls`**

  `array`

  **Items:**

  - **`access_policy`**

    `object` — Access control policy for the QURL

    - **`ai_agent_policy`**

      `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

      - **`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`

  - **`session_duration`**

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

  - **`status`**

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

  - **`use_count`**

    `integer`

* **`resource`**

  `object`

  - **`created_at`**

    `string`, format: `date-time`

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`qurl_count`**

    `integer` — Number of active QURLs for this resource

  - **`resource_id`**

    `string`

  - **`status`**

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

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `string`

**Example:**

```json
{
  "resource": {
    "resource_id": "",
    "target_url": "",
    "status": "active",
    "description": "",
    "tags": [
      ""
    ],
    "custom_domain": null,
    "preserve_host": false,
    "qurl_count": 1,
    "created_at": "",
    "expires_at": ""
  },
  "qurls": [
    {
      "qurl_id": "",
      "label": "",
      "status": "active",
      "one_time_use": true,
      "max_sessions": 1,
      "session_duration": 1,
      "use_count": 1,
      "qurl_site": "",
      "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

  - **`ai_agent_policy`**

    `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

    - **`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`

* **`session_duration`**

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

* **`status`**

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

* **`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": "",
  "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 extend expiration, update 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).

- **`access_policy`**

  `object` — Access control policy for the QURL

  - **`ai_agent_policy`**

    `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

    - **`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` — Maximum concurrent sessions (0 = unlimited).

- **`session_duration`**

  `string` — How long access lasts after clicking. Minimum: 5 minutes. Maximum: 24 hours.

**Example:**

```json
{
  "extend_by": "7d",
  "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

    - **`ai_agent_policy`**

      `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

      - **`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`

  - **`session_duration`**

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

  - **`status`**

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

  - **`use_count`**

    `integer`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "qurl_id": "",
    "label": "",
    "status": "active",
    "one_time_use": true,
    "max_sessions": 1,
    "session_duration": 1,
    "use_count": 1,
    "qurl_site": "",
    "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": ""
  }
}
```

### 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` — Unique request identifier for tracing

**Example:**

```json
{
  "data": [
    {
      "session_id": "",
      "qurl_id": "",
      "src_ip": "",
      "user_agent": "",
      "created_at": "",
      "last_seen_at": ""
    }
  ],
  "meta": {
    "request_id": ""
  }
}
```

### SessionTerminateResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`terminated`**

    `integer` — Number of sessions terminated

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "terminated": 1
  },
  "meta": {
    "request_id": ""
  }
}
```

### ResourceResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`created_at`**

    `string`, format: `date-time`

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`qurl_count`**

    `integer` — Number of active QURLs for this resource

  - **`resource_id`**

    `string`

  - **`status`**

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

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `string`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "resource_id": "",
    "target_url": "",
    "status": "active",
    "description": "",
    "tags": [
      ""
    ],
    "custom_domain": null,
    "preserve_host": false,
    "qurl_count": 1,
    "created_at": "",
    "expires_at": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

### ResourceDetailResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`qurls`**

    `array`

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the QURL

      - **`ai_agent_policy`**

        `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

        - **`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`

    - **`session_duration`**

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

    - **`status`**

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

    - **`use_count`**

      `integer`

  - **`resource`**

    `object`

    - **`created_at`**

      `string`, format: `date-time`

    - **`custom_domain`**

      `string | null`

    - **`description`**

      `string`

    - **`expires_at`**

      `string`, format: `date-time`

    - **`preserve_host`**

      `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

    - **`qurl_count`**

      `integer` — Number of active QURLs for this resource

    - **`resource_id`**

      `string`

    - **`status`**

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

    - **`tags`**

      `array`

      **Items:**

      `string`

    - **`target_url`**

      `string`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "resource": {
      "resource_id": "",
      "target_url": "",
      "status": "active",
      "description": "",
      "tags": [
        ""
      ],
      "custom_domain": null,
      "preserve_host": false,
      "qurl_count": 1,
      "created_at": "",
      "expires_at": ""
    },
    "qurls": [
      {
        "qurl_id": "",
        "label": "",
        "status": "active",
        "one_time_use": true,
        "max_sessions": 1,
        "session_duration": 1,
        "use_count": 1,
        "qurl_site": "",
        "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": ""
  }
}
```

### ResourceListResponse

- **Type:**`object`

* **`data`**

  `array`

  **Items:**

  - **`created_at`**

    `string`, format: `date-time`

  - **`custom_domain`**

    `string | null`

  - **`description`**

    `string`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`preserve_host`**

    `boolean`, default: `false` — Whether to preserve the original Host header when proxying via custom domain

  - **`qurl_count`**

    `integer` — Number of active QURLs for this resource

  - **`resource_id`**

    `string`

  - **`status`**

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

  - **`tags`**

    `array`

    **Items:**

    `string`

  - **`target_url`**

    `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": [
    {
      "resource_id": "",
      "target_url": "",
      "status": "active",
      "description": "",
      "tags": [
        ""
      ],
      "custom_domain": null,
      "preserve_host": false,
      "qurl_count": 1,
      "created_at": "",
      "expires_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### UpdateQurlRequest

- **Type:**`object`

Update a QURL resource. Can extend expiration (via extend\_by or expires\_at) and/or update tags and description. At least one field must be provided.

- **`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": "7d",
  "expires_at": "",
  "tags": [
    ""
  ],
  "description": ""
}
```

### MintLinkRequest

- **Type:**`object`

Optionally provide expires\_in (relative) or expires\_at (absolute) — not both. If neither is specified, defaults to 24 hours from now.

- **`access_policy`**

  `object` — Access control policy for the QURL

  - **`ai_agent_policy`**

    `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

    - **`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 (0 = unlimited)

- **`one_time_use`**

  `boolean`, default: `false` — Whether this QURL can only be used once

- **`session_duration`**

  `string` — How long access lasts after clicking. Minimum: 5 minutes. Maximum: 24 hours.

**Example:**

```json
{
  "expires_in": "5m",
  "expires_at": "",
  "label": "Alice from Acme",
  "one_time_use": false,
  "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"
      ]
    }
  }
}
```

### AccessPolicy

- **Type:**`object`

Access control policy for the QURL

- **`ai_agent_policy`**

  `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

  - **`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`

Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category.

**Important:** This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity.

Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex.

Common examples:

Block all AI agents: { "block\_all": true }

Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] }

Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] }

Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] }

Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

- **`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 container for a protected URL. The `qurls` array (present on single-get responses) contains 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 QURL was created

- **`custom_domain`**

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

- **`description`**

  `string` — Human-readable description

- **`expires_at`**

  `string`, format: `date-time` — When the QURL expires

- **`qurl_count`**

  `integer` — Number of active QURLs (access tokens) for this resource

- **`qurl_site`**

  `string`, format: `uri` — The QURL site URL for accessing this resource

- **`qurls`**

  `array` — Per-QURL details. Present on single-get, omitted on list (too expensive).

  **Items:**

  - **`access_policy`**

    `object` — Access control policy for the QURL

    - **`ai_agent_policy`**

      `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

      - **`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`

  - **`session_duration`**

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

  - **`status`**

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

  - **`use_count`**

    `integer`

- **`resource_id`**

  `string` — Unique resource identifier (generated by server)

- **`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

**Example:**

```json
{
  "resource_id": "",
  "target_url": "",
  "status": "active",
  "created_at": "",
  "expires_at": "",
  "description": "",
  "tags": [
    ""
  ],
  "qurl_site": "",
  "custom_domain": null,
  "qurl_count": 1,
  "qurls": [
    {
      "qurl_id": "",
      "label": "",
      "status": "active",
      "one_time_use": true,
      "max_sessions": 1,
      "session_duration": 1,
      "use_count": 1,
      "qurl_site": "",
      "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"
          ]
        }
      }
    }
  ]
}
```

### CreateQurlData

- **Type:**`object`

Response data for QURL creation

- **`expires_at`**

  `string`, format: `date-time`

- **`label`**

  `string` — Human-readable label for this QURL

- **`qurl_id`**

  `string` — Unique QURL identifier (q\_ + first 11 hex chars of token hash). Used as NHP resource ID and qurl\_site subdomain. Each QURL gets its own firewall rules keyed by this ID.

- **`qurl_link`**

  `string`, format: `uri` — Ephemeral access link (shown once, cannot be recovered)

- **`qurl_site`**

  `string`, format: `uri` — Per-QURL site URL (https\://{qurl\_id}.qurl.site)

- **`resource_id`**

  `string` — Parent resource ID (auto-created grouping by target URL)

**Example:**

```json
{
  "qurl_id": "q_3a7f2c8e91b",
  "resource_id": "",
  "qurl_link": "",
  "qurl_site": "",
  "expires_at": "",
  "label": ""
}
```

### QurlResponse

- **Type:**`object`

* **`data`**

  `object` — Resource container for a protected URL. The \`qurls\` array (present on single-get responses) contains 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 QURL was created

  - **`custom_domain`**

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

  - **`description`**

    `string` — Human-readable description

  - **`expires_at`**

    `string`, format: `date-time` — When the QURL expires

  - **`qurl_count`**

    `integer` — Number of active QURLs (access tokens) for this resource

  - **`qurl_site`**

    `string`, format: `uri` — The QURL site URL for accessing this resource

  - **`qurls`**

    `array` — Per-QURL details. Present on single-get, omitted on list (too expensive).

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the QURL

      - **`ai_agent_policy`**

        `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

        - **`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`

    - **`session_duration`**

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

    - **`status`**

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

    - **`use_count`**

      `integer`

  - **`resource_id`**

    `string` — Unique resource identifier (generated by server)

  - **`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

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "resource_id": "",
    "target_url": "",
    "status": "active",
    "created_at": "",
    "expires_at": "",
    "description": "",
    "tags": [
      ""
    ],
    "qurl_site": "",
    "custom_domain": null,
    "qurl_count": 1,
    "qurls": [
      {
        "qurl_id": "",
        "label": "",
        "status": "active",
        "one_time_use": true,
        "max_sessions": 1,
        "session_duration": 1,
        "use_count": 1,
        "qurl_site": "",
        "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": ""
  }
}
```

### CreateQurlResponse

- **Type:**`object`

* **`data`**

  `object` — Response data for QURL creation

  - **`expires_at`**

    `string`, format: `date-time`

  - **`label`**

    `string` — Human-readable label for this QURL

  - **`qurl_id`**

    `string` — Unique QURL identifier (q\_ + first 11 hex chars of token hash). Used as NHP resource ID and qurl\_site subdomain. Each QURL gets its own firewall rules keyed by this ID.

  - **`qurl_link`**

    `string`, format: `uri` — Ephemeral access link (shown once, cannot be recovered)

  - **`qurl_site`**

    `string`, format: `uri` — Per-QURL site URL (https\://{qurl\_id}.qurl.site)

  - **`resource_id`**

    `string` — Parent resource ID (auto-created grouping by target URL)

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "qurl_id": "q_3a7f2c8e91b",
    "resource_id": "",
    "qurl_link": "",
    "qurl_site": "",
    "expires_at": "",
    "label": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

### 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 firewall access that was granted

    - **`expires_in`**

      `integer` — Seconds until firewall access expires

    - **`granted_at`**

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

    - **`src_ip`**

      `string` — The IP address that was granted access

  - **`resource_id`**

    `string` — QURL resource identifier

  - **`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

**Example:**

```json
{
  "data": {
    "target_url": "https://api.example.com/data",
    "resource_id": "r_k8xqp9h2sj9",
    "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": ""
  }
}
```

### ResolveQurlData

- **Type:**`object`

* **`access_grant`**

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

  - **`expires_in`**

    `integer` — Seconds until firewall access expires

  - **`granted_at`**

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

  - **`src_ip`**

    `string` — The IP address that was granted access

* **`resource_id`**

  `string` — QURL resource identifier

* **`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": "r_k8xqp9h2sj9",
  "access_grant": {
    "expires_in": 305,
    "granted_at": "2026-03-09T15:30:00Z",
    "src_ip": "203.0.113.42"
  }
}
```

### AccessGrant

- **Type:**`object`

Details of the firewall access that was granted

- **`expires_in`**

  `integer` — Seconds until firewall access expires

- **`granted_at`**

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

- **`src_ip`**

  `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 QURL was created

  - **`custom_domain`**

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

  - **`description`**

    `string` — Human-readable description

  - **`expires_at`**

    `string`, format: `date-time` — When the QURL expires

  - **`qurl_count`**

    `integer` — Number of active QURLs (access tokens) for this resource

  - **`qurl_site`**

    `string`, format: `uri` — The QURL site URL for accessing this resource

  - **`qurls`**

    `array` — Per-QURL details. Present on single-get, omitted on list (too expensive).

    **Items:**

    - **`access_policy`**

      `object` — Access control policy for the QURL

      - **`ai_agent_policy`**

        `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

        - **`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`

    - **`session_duration`**

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

    - **`status`**

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

    - **`use_count`**

      `integer`

  - **`resource_id`**

    `string` — Unique resource identifier (generated by server)

  - **`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

* **`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": "",
      "target_url": "",
      "status": "active",
      "created_at": "",
      "expires_at": "",
      "description": "",
      "tags": [
        ""
      ],
      "qurl_site": "",
      "custom_domain": null,
      "qurl_count": 1,
      "qurls": [
        {
          "qurl_id": "",
          "label": "",
          "status": "active",
          "one_time_use": true,
          "max_sessions": 1,
          "session_duration": 1,
          "use_count": 1,
          "qurl_site": "",
          "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": ""
  }
}
```

### MintLinkData

- **Type:**`object`

* **`expires_at`**

  `string`, format: `date-time`

* **`qurl_link`**

  `string`, format: `uri` — Single-use access link

**Example:**

```json
{
  "qurl_link": "",
  "expires_at": ""
}
```

### MintLinkResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`expires_at`**

    `string`, format: `date-time`

  - **`qurl_link`**

    `string`, format: `uri` — Single-use access link

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "qurl_link": "",
    "expires_at": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

### BatchCreateRequest

- **Type:**`object`

* **`items` (required)**

  `array` — Array of QURL creation requests (1-100 items)

  **Items:**

  - **`target_url` (required)**

    `string`, format: `uri` — The URL to protect with QURL (max 2048 characters)

  - **`access_policy`**

    `object` — Access control policy for the QURL

    - **`ai_agent_policy`**

      `object` — Structured policy for controlling AI agent access. Users select well-known categories by name instead of crafting regex patterns. The server maintains the user-agent patterns for each category. \*\*Important:\*\* This blocks agents that self-identify via their User-Agent header. It does not prevent agents that spoof or omit their identity. Evaluation order: AI agent policy is evaluated after IP/Geo rules but before generic user\_agent\_deny\_regex / user\_agent\_allow\_regex. A bot allowed by this policy can still be blocked by user\_agent\_deny\_regex. A bot blocked here cannot be rescued by user\_agent\_allow\_regex. Common examples: Block all AI agents: { "block\_all": true } Block specific crawlers (e.g., GPTBot and Common Crawl) while allowing others: { "deny\_categories": \["gptbot", "commoncrawl"] } Allow only Claude and Perplexity, block all other AI agents: { "allow\_categories": \["claude", "perplexity"] } Block ByteDance and Meta crawlers but allow ChatGPT and Claude: { "deny\_categories": \["bytedance", "meta"], "allow\_categories": \["chatgpt", "claude"] } Note: deny always takes precedence over allow. If an agent appears in both lists, it is blocked.

      - **`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` — Duration until expiration. Supports: hours (24h), days (7d), weeks (1w). Minimum: 1 minute. Maximum varies by plan: free=3 days, growth/enterprise=30 days. Default: 24h.

  - **`label`**

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

  - **`max_sessions`**

    `integer` — Maximum concurrent sessions allowed. 0 = unlimited (default), 1-1000 = hard limit.

  - **`one_time_use`**

    `boolean`, default: `false` — Whether this QURL expires after a single use

  - **`session_duration`**

    `string` — How long access lasts after someone clicks this QURL. Controls the session/cookie lifetime, separate from the QURL link expiration (expires\_in). Supports: minutes (5m), hours (1h), days (1d). Minimum: 5 minutes. Maximum: 24 hours. Default: server default (typically 1 hour).

**Example:**

```json
{
  "items": [
    {
      "target_url": "https://internal.example.com/dashboard",
      "expires_in": "7d",
      "one_time_use": false,
      "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:**

    - **`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` — QURL site URL (if success)

    - **`resource_id`**

      `string` — Created resource ID (if success)

    - **`success`**

      `boolean`

  - **`succeeded`**

    `integer` — Number of successfully created QURLs

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "succeeded": 1,
    "failed": 1,
    "results": [
      {
        "index": 1,
        "success": true,
        "resource_id": "",
        "qurl_link": "",
        "qurl_site": "",
        "expires_at": "",
        "error": {
          "code": "",
          "message": ""
        }
      }
    ]
  },
  "meta": {
    "request_id": ""
  }
}
```

### BatchItemResult

- **Type:**`object`

* **`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` — QURL site URL (if success)

* **`resource_id`**

  `string` — Created resource ID (if success)

* **`success`**

  `boolean`

**Example:**

```json
{
  "index": 1,
  "success": true,
  "resource_id": "",
  "qurl_link": "",
  "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` — Maximum active QURLs allowed (-1 = unlimited)

  - **`max_expiry_seconds`**

    `integer` — Maximum expiry duration in seconds (free=259200/3d, growth=2592000/30d, enterprise=2592000/30d)

  - **`max_tokens_per_qurl`**

    `integer` — Maximum tokens per QURL (-1 = unlimited)

  - **`resolve_per_minute`**

    `integer` — Rate limit for internal token resolution (used by access infrastructure)

* **`usage`**

  `object`

  - **`active_qurls`**

    `integer` — Currently active QURLs

  - **`active_qurls_percent`**

    `number | null`, format: `float` — Percentage of max\_active\_qurls used (0-100, null if unlimited)

  - **`qurls_created`**

    `integer` — Total QURLs created this period

  - **`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_active_qurls": 1,
    "max_tokens_per_qurl": 1,
    "max_expiry_seconds": 1
  },
  "usage": {
    "qurls_created": 1,
    "active_qurls": 1,
    "active_qurls_percent": null,
    "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` — Maximum active QURLs allowed (-1 = unlimited)

    - **`max_expiry_seconds`**

      `integer` — Maximum expiry duration in seconds (free=259200/3d, growth=2592000/30d, enterprise=2592000/30d)

    - **`max_tokens_per_qurl`**

      `integer` — Maximum tokens per QURL (-1 = unlimited)

    - **`resolve_per_minute`**

      `integer` — Rate limit for internal token resolution (used by access infrastructure)

  - **`usage`**

    `object`

    - **`active_qurls`**

      `integer` — Currently active QURLs

    - **`active_qurls_percent`**

      `number | null`, format: `float` — Percentage of max\_active\_qurls used (0-100, null if unlimited)

    - **`qurls_created`**

      `integer` — Total QURLs created this period

    - **`total_accesses`**

      `integer` — Total access attempts this period

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**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_active_qurls": 1,
      "max_tokens_per_qurl": 1,
      "max_expiry_seconds": 1
    },
    "usage": {
      "qurls_created": 1,
      "active_qurls": 1,
      "active_qurls_percent": null,
      "total_accesses": 1
    }
  },
  "meta": {
    "request_id": ""
  }
}
```

### UsageCostEstimate

- **Type:**`object`

* **`amount_cents` (required)**

  `integer` — 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` — Number of currently active (non-expired, non-revoked) QURLs

* **`period_end` (required)**

  `string`, format: `date-time` — End of the current billing period (UTC)

* **`period_start` (required)**

  `string`, format: `date-time` — Start of the current billing period (UTC)

* **`qurls_created` (required)**

  `integer` — Number of QURLs created in this period

* **`tier` (required)**

  `string`, possible values: `"free", "growth", "enterprise"` — Current billing tier

* **`cost_estimate`**

  `object`

  - **`amount_cents` (required)**

    `integer` — 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,
  "active_qurls": 1,
  "cost_estimate": {
    "currency": "usd",
    "amount_cents": 1500,
    "description": "150 QURLs x $0.10/QURL"
  }
}
```

### UsageCurrentPeriodResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`active_qurls` (required)**

    `integer` — Number of currently active (non-expired, non-revoked) QURLs

  - **`period_end` (required)**

    `string`, format: `date-time` — End of the current billing period (UTC)

  - **`period_start` (required)**

    `string`, format: `date-time` — Start of the current billing period (UTC)

  - **`qurls_created` (required)**

    `integer` — Number of QURLs created in this period

  - **`tier` (required)**

    `string`, possible values: `"free", "growth", "enterprise"` — Current billing tier

  - **`cost_estimate`**

    `object`

    - **`amount_cents` (required)**

      `integer` — 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

**Example:**

```json
{
  "data": {
    "tier": "free",
    "period_start": "",
    "period_end": "",
    "qurls_created": 1,
    "active_qurls": 1,
    "cost_estimate": {
      "currency": "usd",
      "amount_cents": 1500,
      "description": "150 QURLs x $0.10/QURL"
    }
  },
  "meta": {
    "request_id": ""
  }
}
```

### UsageDailyEntry

- **Type:**`object`

* **`date` (required)**

  `string`, format: `date` — Calendar date (YYYY-MM-DD)

* **`qurls_created` (required)**

  `integer` — QURLs created on this date

**Example:**

```json
{
  "date": "",
  "qurls_created": 1
}
```

### UsageDailyData

- **Type:**`object`

* **`daily` (required)**

  `array` — One entry per day from period\_start to today

  **Items:**

  - **`date` (required)**

    `string`, format: `date` — Calendar date (YYYY-MM-DD)

  - **`qurls_created` (required)**

    `integer` — QURLs created on this date

* **`period_end` (required)**

  `string`, format: `date-time` — End of the current billing period (UTC)

* **`period_start` (required)**

  `string`, format: `date-time` — Start of the current billing period (UTC)

* **`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 day from period\_start to today

    **Items:**

    - **`date` (required)**

      `string`, format: `date` — Calendar date (YYYY-MM-DD)

    - **`qurls_created` (required)**

      `integer` — QURLs created on this date

  - **`period_end` (required)**

    `string`, format: `date-time` — End of the current billing period (UTC)

  - **`period_start` (required)**

    `string`, format: `date-time` — Start of the current billing period (UTC)

  - **`tier` (required)**

    `string`, possible values: `"free", "growth", "enterprise"` — Current billing tier

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "tier": "free",
    "period_start": "",
    "period_end": "",
    "daily": [
      {
        "date": "",
        "qurls_created": 1
      }
    ]
  },
  "meta": {
    "request_id": ""
  }
}
```

### CreateApiKeyRequest

- **Type:**`object`

* **`name` (required)**

  `string` — Human-readable name for the API key

* **`scopes` (required)**

  `array` — Permissions granted to this API key

  **Items:**

  `string`, possible values: `"qurl:read", "qurl:write", "qurl:resolve"`

**Example:**

```json
{
  "name": "",
  "scopes": [
    "qurl:read"
  ]
}
```

### UpdateApiKeyRequest

- **Type:**`object`

* **`name`**

  `string` — Updated name for the API key

* **`scopes`**

  `array` — Updated permissions for the API 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"`

**Example:**

```json
{
  "name": "",
  "scopes": [
    "qurl:read"
  ]
}
```

### ApiKeyData

- **Type:**`object`

* **`created_at`**

  `string`, format: `date-time`

* **`key_id`**

  `string`

* **`key_prefix`**

  `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

* **`last_used_at`**

  `string`, format: `date-time`

* **`name`**

  `string`

* **`scopes`**

  `array` — Permissions granted to this API key. Returned in alphabetical order for deterministic responses.

  **Items:**

  `string`

* **`status`**

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

* **`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": "",
  "name": "",
  "scopes": [
    ""
  ],
  "status": "active",
  "created_at": "",
  "updated_at": "",
  "last_used_at": ""
}
```

### CreateApiKeyData

- **Type:**

**Example:**

### CreateApiKeyResponse

- **Type:**`object`

* **`data`**

  `object`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "key_id": "",
    "key_prefix": "",
    "name": "",
    "scopes": [
      ""
    ],
    "status": "active",
    "created_at": "",
    "updated_at": "",
    "last_used_at": "",
    "api_key": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

### ApiKeyResponse

- **Type:**`object`

* **`data`**

  `object`

  - **`created_at`**

    `string`, format: `date-time`

  - **`key_id`**

    `string`

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

  - **`last_used_at`**

    `string`, format: `date-time`

  - **`name`**

    `string`

  - **`scopes`**

    `array` — Permissions granted to this API key. Returned in alphabetical order for deterministic responses.

    **Items:**

    `string`

  - **`status`**

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

  - **`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

**Example:**

```json
{
  "data": {
    "key_id": "",
    "key_prefix": "",
    "name": "",
    "scopes": [
      ""
    ],
    "status": "active",
    "created_at": "",
    "updated_at": "",
    "last_used_at": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

### ApiKeyListResponse

- **Type:**`object`

* **`data`**

  `array`

  **Items:**

  - **`created_at`**

    `string`, format: `date-time`

  - **`key_id`**

    `string`

  - **`key_prefix`**

    `string` — First 12 characters of the key for identification. Includes the environment prefix (e.g., "lv\_live\_a3x9").

  - **`last_used_at`**

    `string`, format: `date-time`

  - **`name`**

    `string`

  - **`scopes`**

    `array` — Permissions granted to this API key. Returned in alphabetical order for deterministic responses.

    **Items:**

    `string`

  - **`status`**

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

  - **`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": "",
      "name": "",
      "scopes": [
        ""
      ],
      "status": "active",
      "created_at": "",
      "updated_at": "",
      "last_used_at": ""
    }
  ],
  "meta": {
    "request_id": "",
    "page_size": 1,
    "has_more": true,
    "next_cursor": ""
  }
}
```

### 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

**Example:**

```json
{
  "data": {
    "redirect_url": "https://qurl.link/#at_k8xqp9h2sj9lx7r4abcdef"
  },
  "meta": {
    "request_id": ""
  }
}
```

### CreateAccessCodeRequest

- **Type:**`object`

* **`resource_id` (required)**

  `string` — ID of the QURL resource this code grants access to

* **`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": "r_k8xqp9h2sj9",
  "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` — ID of the QURL 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": "r_k8xqp9h2sj9",
  "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

**Example:**

```json
{
  "data": {
    "access_code_id": "acd_k8xqp9h2sj9",
    "resource_id": "r_k8xqp9h2sj9",
    "name": "Investor Stats Q1",
    "status": "active",
    "max_uses": 10,
    "use_count": 3,
    "created_at": "",
    "expires_at": null,
    "code": "ac_k8xqp9h2sj9lx7r4abcdef"
  },
  "meta": {
    "request_id": ""
  }
}
```

### 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` — ID of the QURL 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

**Example:**

```json
{
  "data": [
    {
      "access_code_id": "acd_k8xqp9h2sj9",
      "resource_id": "r_k8xqp9h2sj9",
      "name": "Investor Stats Q1",
      "status": "active",
      "max_uses": 10,
      "use_count": 3,
      "created_at": "",
      "expires_at": null
    }
  ],
  "meta": {
    "request_id": ""
  }
}
```

### WebhookEventType

- **Type:**`string`

Webhook event types:

- **qurl.created** - A new QURL was created
- **qurl.expired** - A QURL has expired
- **qurl.revoked** - A QURL was manually revoked
- **qurl.updated** - A QURL was updated (expiration, tags, or description)
- **qurl.accessed** - A QURL was successfully accessed
- **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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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

* **`status`**

  `string`, possible values: `"active", "disabled"`

* **`updated_at`**

  `string`, format: `date-time`

* **`url`**

  `string`, format: `uri`

* **`webhook_id`**

  `string`

**Example:**

```json
{
  "webhook_id": "",
  "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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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

  - **`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

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "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": ""
  }
}
```

### WebhookResponseWithSecret

- **Type:**`object`

* **`data`**

  `object`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "data": {
    "webhook_id": "",
    "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": ""
  }
}
```

### 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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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

  - **`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": "",
      "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`

* **`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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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` — Truncated response body (max 8KB)

* **`response_code`**

  `integer`

* **`retry_count`**

  `integer`

* **`status`**

  `string`, possible values: `"pending", "success", "failed", "retrying", "abandoned"`

* **`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`

  - **`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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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` — Truncated response body (max 8KB)

  - **`response_code`**

    `integer`

  - **`retry_count`**

    `integer`

  - **`status`**

    `string`, possible values: `"pending", "success", "failed", "retrying", "abandoned"`

  - **`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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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

**Example:**

```json
{
  "data": [
    {
      "type": "qurl.created",
      "category": "resource",
      "description": ""
    }
  ],
  "meta": {
    "request_id": ""
  }
}
```

### WebhookPayload

- **Type:**`object`

Webhook event payload sent to your endpoint. Verify authenticity using the QURL-Signature header.

- **`api_version`**

  `string` — API version (e.g., "2024-01-01")

- **`data`**

  `object` — Event-specific data

- **`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", "qurl.accessed", "qurl.access_denied", "qurl.token_exhausted", "quota.warning", "quota.exceeded", "token.minted", "token.expired", "domain.verified", "domain.failed", "domain.deleted"` — Webhook event types: - \*\*qurl.created\*\* - A new QURL was created - \*\*qurl.expired\*\* - A QURL has expired - \*\*qurl.revoked\*\* - A QURL was manually revoked - \*\*qurl.updated\*\* - A QURL was updated (expiration, tags, or description) - \*\*qurl.accessed\*\* - A QURL was successfully accessed - \*\*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": {
    "resource_id": "r_k8xqp9h2sj9",
    "access_token_id": "at_xyz789",
    "src_ip": "192.168.1.100",
    "user_agent": "Mozilla/5.0..."
  }
}
```

### CustomerData

- **Type:**`object`

* **`current_period_usage` (required)**

  `integer`, format: `int64` — Usage count in current billing period

* **`frozen` (required)**

  `boolean` — Whether the account is frozen

* **`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
{
  "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

  - **`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

**Example:**

```json
{
  "data": {
    "tier": "free",
    "spending_cap_cents": 1,
    "current_period_usage": 1,
    "frozen": true,
    "frozen_reason": "spending_cap"
  },
  "meta": {
    "request_id": ""
  }
}
```

### 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 at the OpenAPI middleware layer as an invalid enum value before the handler runs.

**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

**Example:**

```json
{
  "data": {
    "url": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

### 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

**Example:**

```json
{
  "data": {
    "url": ""
  },
  "meta": {
    "request_id": ""
  }
}
```

### 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

**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": ""
  }
}
```

### 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

**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": ""
  }
}
```

### Meta

- **Type:**`object`

* **`request_id` (required)**

  `string` — Unique request identifier for tracing

**Example:**

```json
{
  "request_id": ""
}
```

### 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

**Example:**

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

### ValidationErrorEnvelope

- **Type:**`object`

* **`error`**

  `object`

* **`meta`**

  `object`

  - **`request_id` (required)**

    `string` — Unique request identifier for tracing

**Example:**

```json
{
  "error": {
    "type": "https://api.qurl.link/problems/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": ""
  }
}
```
