# qURL™ Service API

> API version 1.0.0 · OpenAPI 3.0.3

Source: https://layerv.ai/docs/

---

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

## Start here

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

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

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

## Quickstart: register, share, and revoke

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

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

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

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

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

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

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

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

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

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

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

## Choose an operation

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

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

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

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

## Authentication

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

```http
Authorization: Bearer YOUR_API_KEY_OR_ACCESS_TOKEN
```

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

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

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

## Retries and idempotency

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

## Responses and errors

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

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

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

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

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

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

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

## Rate limits and caching

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

## Webhooks

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

## Base URLs

- `https://api.layerv.ai` — Production

## Operations

### Account

- `GET /v1/account/auth` — Get browser sign-in settings
- `POST /v1/account/link` — Link this device to an account
- `GET /v1/account/owners` — List account resource owners

### Resources

- `GET /v1/resources` — List resources
- `POST /v1/resources` — Create a resource
- `GET /v1/resources/{id}` — Get resource details
- `PATCH /v1/resources/{id}` — Update resource metadata
- `DELETE /v1/resources/{id}` — Revoke resource and all its qURLs
- `GET /v1/crid-preview-pages/{crid}` — Read the qURL page with published preview text
- `GET /v1/crid-previews/{token}` — Read a CRID card preview
- `GET /v1/resources/{id}/preview` — Read one CRID preview
- `PATCH /v1/resources/{id}/preview` — Edit one CRID preview
- `POST /v1/resources/{id}/preview/uploads` — Upload media for a verified publisher
- `GET /v1/resources/{id}/sharing` — Get qURL Connector sharing status
- `PUT /v1/resources/{id}/sharing` — Start or stop qURL Connector sharing
- `POST /v1/resources/{id}/sharing/restart` — Restart qURL Connector sharing
- `GET /v1/resources/{id}/invitations` — listConsumerInvitations
- `POST /v1/resources/{id}/invitations` — inviteConsumer
- `POST /v1/resources/{id}/invitations/{invitation_id}/resend` — resendConsumerInvitation
- `DELETE /v1/resources/{id}/invitations/{invitation_id}` — revokeConsumerInvitation
- `GET /v1/resources/{id}/consumer-grants` — listConsumerGrants
- `POST /v1/resources/{id}/consumer-grants/{grant_id}/approve` — approveConsumerGrant
- `POST /v1/resources/{id}/consumer-grants/{grant_id}/deny` — denyConsumerGrant
- `DELETE /v1/resources/{id}/consumer-grants/{grant_id}` — revokeConsumerGrant
- `GET /v1/resources/{id}/access-requests` — List pending access requests for a resource
- `DELETE /v1/resources/{id}/access-requests/{device_id}` — Deny an access request
- `POST /v1/resources/{id}/access-requests/{request_code}/approve` — Approve an access request
- `DELETE /v1/resources/{id}/allowed-passkeys/{device_id}` — Remove an approved passkey
- `GET /v1/access-requests` — List pending access requests for all your resources
- `GET /v1/resources/{id}/qurls` — List qURLs for a resource
- `POST /v1/resources/{id}/qurls` — Create a qURL for an existing resource
- `PATCH /v1/resources/{id}/qurls/{qurl_id}` — Update a specific qURL
- `DELETE /v1/resources/{id}/qurls/{qurl_id}` — Revoke a specific qURL
- `POST /v1/resources/{id}/share` — Share a resource by CRID

### QURLs

- `GET /v1/qurls` — List resources
- `POST /v1/qurls` — Create an access link from a URL
- `GET /v1/qurls/{id}` — Get a resource
- `PATCH /v1/qurls/{id}` — Update a resource
- `DELETE /v1/qurls/{id}` — Revoke a resource and all its qURLs
- `POST /v1/qurls/{id}/mint_link` — Mint an access link
- `POST /v1/qurls/batch` — Batch create qURLs
- `POST /v1/delegated-qurl-batches` — Create qURLs from a delegated mint capability
- `GET /v1/delegated-qurl-batches/{batch_id}` — Get a delegated qURL batch
- `GET /v1/delegated-qurls/{qurl_id}` — Get delegated qURL metadata
- `DELETE /v1/delegated-qurls/{qurl_id}` — Revoke a delegated qURL

### Quota

- `GET /v1/quota` — Get quota information

### Identity

- `GET /v1/me` — Get the authenticated identity

### Publisher

- `GET /v1/me/publisher` — Get your publisher profile
- `PATCH /v1/me/publisher` — Set or remove your publisher name

### Webhooks

- `GET /v1/webhooks` — List webhooks
- `POST /v1/webhooks` — Create a webhook
- `GET /v1/webhooks/events` — List available event types
- `GET /v1/webhooks/{id}` — Get a webhook
- `PATCH /v1/webhooks/{id}` — Update a webhook
- `DELETE /v1/webhooks/{id}` — Delete a webhook
- `POST /v1/webhooks/{id}/secret` — Regenerate webhook secret
- `GET /v1/webhooks/{id}/deliveries` — List webhook deliveries

### API Keys

- `GET /v1/api-keys` — List API keys
- `POST /v1/api-keys` — Create an API key or enrollment token
- `GET /v1/api-keys/{key_id}` — Get a credential
- `PATCH /v1/api-keys/{key_id}` — Update an API key
- `DELETE /v1/api-keys/{key_id}` — Revoke a credential

### Usage

- `GET /v1/usage/current-period` — Get current billing period usage
- `GET /v1/usage/daily` — Get daily usage breakdown

### Customer

- `PUT /v1/me/signup-attribution` — Record signup discovery source
- `GET /v1/me/favorites` — Get your favorites
- `PUT /v1/me/favorites` — Replace your favorites
- `GET /v1/me/preferences` — Get your preferences
- `PATCH /v1/me/preferences` — Change your preferences
- `GET /v1/customer` — Get customer profile
- `PATCH /v1/customer` — Update customer settings

### Billing

- `POST /v1/billing/checkout` — Create Stripe checkout session
- `POST /v1/billing/portal` — Create Stripe billing portal session
- `GET /v1/billing/invoices` — List invoices

### Resolution

- `POST /v1/resolve` — Redeem an access token without a browser

### Domains

- `GET /v1/domains` — List domains
- `POST /v1/domains` — Register a custom domain
- `GET /v1/domains/{domain}` — Get domain status
- `DELETE /v1/domains/{domain}` — Remove domain
- `POST /v1/domains/{domain}/verify` — Trigger DNS verification
- `POST /v1/domains/{domain}/regenerate-token` — Regenerate verification token

### Access Codes

- `POST /v1/access-codes/redeem` — Redeem an access code
- `GET /v1/access-codes` — List access codes
- `POST /v1/access-codes` — Create an access code
- `DELETE /v1/access-codes/{id}` — Revoke an access code

### Connectors

- `GET /v1/connectors/installations` — List connector installations
- `POST /v1/connectors/agents/{agent_id}/recovery-token` — Authorize device credential recovery
- `GET /v1/connectors/devices` — List Connector devices
- `PATCH /v1/connectors/devices/{agent_id}` — Rename a Connector device
- `DELETE /v1/connectors/agents/{agent_id}/assignment` — Retire a Connector assignment

### Sessions

- `GET /v1/resources/{id}/sessions` — List active sessions for a resource
- `DELETE /v1/resources/{id}/sessions` — Terminate all active sessions for a resource
- `DELETE /v1/resources/{id}/sessions/{session_id}` — Terminate a specific session

### Bindings

- `POST /v1/external-identity-bindings` — Create or rotate an external identity binding
- `DELETE /v1/external-identity-bindings/{binding_id}` — Delete an external identity binding

## Full reference

This page is a compact index. Every parameter, schema, and response example lives in the complete reference:

- Markdown: https://layerv.ai/docs/qurls.md (~759k tokens — fetch selectively)
- OpenAPI YAML: https://layerv.ai/docs/qurls.yaml
- OpenAPI JSON: https://layerv.ai/docs/qurls.json
- Curated agent entry point: https://layerv.ai/llms.txt

