> ## Documentation Index
> Fetch the complete documentation index at: https://learn.biq.li/llms.txt
> Use this file to discover all available pages before exploring further.

# Update a link

> Partially update specific fields on an existing short link.

Update only the fields you send. Omitted fields keep their current values, so
you can make surgical changes without resending the complete link.

<Note>
  The public API uses `PATCH`. The Biqli dashboard's internal `PUT` request is
  not part of this API contract.
</Note>

## Authentication and permissions

The key needs **Links: Write** (`links.update`). If you attach a non-empty list
of existing workspace resources, it also needs the matching read permission.

| Request field | Additional API key permission                      |
| ------------- | -------------------------------------------------- |
| `domain_id`   | **Custom domains: Read** (`custom_domains.view`)   |
| `folder_ids`  | **Folders: Read** (`link_groups.view`)             |
| `pixel_ids`   | **Tracking pixels: Read** (`tracking_pixels.view`) |
| `tag_ids`     | **Tags: Read** (`tags.view`)                       |

## Update one field

```bash theme={null}
curl --request PATCH \
  --url https://biq.li/api/v1/link/biq_lnk_01M14D2M8VFKYQCE7Z3A6HRXWP \
  --header 'Authorization: Bearer biqli_your_workspace_api_key' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
    "password": "new-private-password"
  }'
```

This changes only the password. Send `null` to remove nullable values such as a
password, external ID, custom alias, schedule, UTM string, or click-expiration
rule.

## Writable fields

PATCH accepts every mutable create field except `create_qr_code` and `qr_logo`.
QR generation is a creation-only option.

| Group                    | Fields                                                            |
| ------------------------ | ----------------------------------------------------------------- |
| Identity and destination | `long_url`, `name`, `external_id`, `domain_id`, `alias`, `active` |
| Access and expiration    | `password`, `activates_at`, `expires_at`, `exp_clicks_rule`       |
| Redirect behavior        | `utm`, `geo_rules`, `device_rules`, `platform_rules`              |
| Attachments              | `folder_ids`, `pixel_ids`, `tag_ids`                              |
| Tracking and indexing    | `conversion_tracking_enabled`, `allow_search_engine_indexing`     |
| Social preview           | `proxy`, `title`, `description`, `image`                          |

The types, limits, URL safety checks, plan restrictions, and public-ID formats
are identical to [Create a link](/docs/api-reference/links/create).

## Replacement and clearing rules

* Sending an array replaces that complete array. Send `[]` to detach all
  folders, pixels, or tags, or to remove every rule in one targeting category.
* Omitting an array leaves its current values unchanged.
* Sending `domain_id: null` switches the link to the default Biqli domain.
* Sending `proxy: false` disables the custom preview and clears its title,
  description, and image.
* When `proxy` remains enabled, omitted preview fields keep their current values.
* Sending `exp_clicks_rule: null` removes click-based expiration.
* The API never accepts internal numeric IDs or workspace IDs.

When changing `alias` or `domain_id`, Biqli validates the effective alias and
domain together. The link's own current alias is excluded from the uniqueness
check.

## Update several settings

```json theme={null}
{
  "long_url": "https://example.com/autumn-product",
  "external_id": "campaign-link-1842",
  "alias": "autumn-product",
  "expires_at": "2026-12-01T08:00:00Z",
  "geo_rules": [],
  "folder_ids": [
    "biq_fld_01M14C93RJD7W46PFJX9B1TVYH"
  ],
  "proxy": true,
  "title": "Autumn sale"
}
```

Changing any destination, including dynamic-routing destinations, runs the same
synchronous and asynchronous safety checks as creation. A destination needing a
deeper check can set `safety_status` to `pending`; the short URL will not redirect
until that check clears.

## Response

A successful update returns `200 OK` and the complete current link resource,
including untouched configuration and current aggregate performance data.

```json theme={null}
{
  "link": {
    "id": "biq_lnk_01M14D2M8VFKYQCE7Z3A6HRXWP",
    "external_id": "campaign-link-1842",
    "short_url": "https://biq.li/autumn-product",
    "long_url": "https://example.com/autumn-product",
    "name": "Summer campaign",
    "domain_id": null,
    "alias": "autumn-product",
    "active": true,
    "has_password": true,
    "activates_at": null,
    "expires_at": "2026-12-01T08:00:00+00:00",
    "exp_clicks_rule": null,
    "utm": null,
    "geo_rules": [],
    "device_rules": [],
    "platform_rules": [],
    "folder_ids": ["biq_fld_01M14C93RJD7W46PFJX9B1TVYH"],
    "pixel_ids": [],
    "tag_ids": [],
    "conversion_tracking_enabled": true,
    "allow_search_engine_indexing": false,
    "proxy": true,
    "title": "Autumn sale",
    "description": "Explore the summer collection.",
    "image": "https://cdn.example.com/previews/summer.jpg",
    "qr_code": null,
    "clicks_count": 2841,
    "leads_count": 93,
    "sales_count": 27,
    "revenue": 4148.5,
    "revenue_currency": "USD",
    "clicked_at": "2026-08-29T10:42:11+00:00",
    "safety_status": "clear",
    "created_at": "2026-08-28T16:42:19+00:00",
    "updated_at": "2026-08-29T11:03:44+00:00"
  },
  "status": "success"
}
```

An empty object is a valid no-op and returns the current resource.

## Errors

| HTTP status | Error code              | Meaning                                                                           |
| ----------- | ----------------------- | --------------------------------------------------------------------------------- |
| `401`       | `invalid_token`         | The API key is missing, invalid, malformed, or revoked.                           |
| `403`       | `insufficient_scope`    | The key lacks `links.update`, an attachment read permission, or workspace access. |
| `403`       | `upgrade_required`      | The workspace plan does not include a requested feature.                          |
| `404`       | `resource_not_found`    | The link or an attached public resource is unavailable in this workspace.         |
| `409`       | `alias_taken`           | The effective alias is already used on the effective domain.                      |
| `409`       | `external_id_taken`     | The external ID is already used in the workspace.                                 |
| `422`       | `url_blocked`           | A supplied destination failed a synchronous safety check.                         |
| `422`       | `validation_error`      | A field is invalid, unknown, or inconsistent with another field.                  |
| `429`       | `rate_limit_exceeded`   | The workspace exceeded its API request limit.                                     |
| `500`       | `internal_server_error` | An unexpected server error occurred.                                              |

The API rejects unknown fields and returns validation details keyed by field.

## Shared API behavior

Authentication is workspace-scoped; see [Authentication](/docs/api-reference/authentication). Errors use the standard envelope and request IDs described in [Errors](/docs/api-reference/errors), and requests are subject to [Rate limits](/docs/api-reference/rate-limits).


## OpenAPI

````yaml PATCH /v1/link/{link}
openapi: 3.1.0
info:
  title: Biqli API
  version: 1.0.0
  description: Workspace-scoped REST API for Biqli.
servers:
  - url: https://biq.li/api
security:
  - bearerAuth: []
paths:
  /v1/link/{link}:
    patch:
      tags:
        - Links
      summary: Update a link
      description: >-
        Partially updates only the supplied mutable fields and returns the
        complete current link resource. Requires links.update.
      operationId: link.update
      parameters:
        - name: link
          in: path
          required: true
          description: Public link ID.
          schema:
            type: string
            pattern: ^biq_lnk_[0-9A-HJKMNP-TV-Z]{26}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateLinkRequest'
            examples:
              passwordOnly:
                summary: Change only the password
                value:
                  password: new-private-password
              clearRules:
                summary: Clear geo rules without changing other rule types
                value:
                  geo_rules: []
      responses:
        '200':
          description: Link updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LinkResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  schemas:
    UpdateLinkRequest:
      type: object
      description: A partial link update. Omitted properties keep their current values.
      additionalProperties: false
      properties:
        long_url: 05436939-163d-42a1-a46b-5febe8972185
        name: 8fd3050f-54e4-43d9-a589-934210f4ad4e
        external_id: a725f1c6-2c3c-4503-84c0-08fa0758b839
        domain_id: 5fe841dd-ba54-4189-9515-5dd6e9a4c58a
        alias: 0cb4add7-52ce-4cc6-814d-647ef9bd2715
        active: 176f58cb-82e0-4f76-b6d8-9055e5ad8808
        password: 1add5038-51dd-4efc-9b7f-0176dbe88bfd
        activates_at: 711f9940-41a6-436b-b343-13c993c12d42
        expires_at: ccf4d194-8bcf-4d23-972d-8937fe827e47
        exp_clicks_rule: 59d8f321-bc32-47f2-9714-925df33fa868
        utm: e20022e1-62de-43b1-8e9f-7b05bf5ae954
        geo_rules: f190bfac-b5ee-4ac9-b260-9e205020fd08
        device_rules: feaa0c92-a9a1-4dfb-a452-a642e01da2bc
        platform_rules: ce030e0a-f823-445a-809b-37dedbfb0298
        folder_ids: 5e09847e-09c5-4e82-9c3f-b45fa680750c
        pixel_ids: ed8d8232-987c-4079-a761-5c43d796d671
        tag_ids: 2b365fd6-708a-4bbd-b26f-b5effe279779
        conversion_tracking_enabled: b17ab153-e55d-479d-998a-9a01bf2b0da6
        allow_search_engine_indexing: 0dd79ef7-3306-4a9c-8e3b-8285b8fe131e
        proxy: e47b69a2-b5c6-4c3d-9aff-06ca5421b1f9
        title: 4f148443-6b51-4c3c-953e-bab66aac6184
        description: cebf8337-babe-48dc-ba19-97ce1948eb39
        image: 4c659d58-b3c1-42e9-825e-12745be9bb77
    LinkResponse:
      type: object
      required:
        - link
        - status
      properties:
        link:
          $ref: '#/components/schemas/CreatedLink'
        status:
          type: string
          const: success
    CreatedLink:
      type: object
      required:
        - id
        - external_id
        - short_url
        - long_url
        - name
        - domain_id
        - alias
        - active
        - has_password
        - activates_at
        - expires_at
        - exp_clicks_rule
        - utm
        - folder_ids
        - pixel_ids
        - tag_ids
        - conversion_tracking_enabled
        - allow_search_engine_indexing
        - proxy
        - title
        - description
        - image
        - qr_code
        - geo_rules
        - device_rules
        - platform_rules
        - clicks_count
        - leads_count
        - sales_count
        - revenue
        - revenue_currency
        - clicked_at
        - safety_status
        - created_at
        - updated_at
      properties:
        id:
          type: string
          description: Public link ID. Internal numeric IDs are never returned.
          pattern: ^biq_lnk_[0-9A-HJKMNP-TV-Z]{26}$
        external_id:
          type:
            - string
            - 'null'
          description: Caller-controlled identifier, unique within the workspace.
        short_url:
          type: string
          format: uri
        long_url:
          type: string
        name:
          type:
            - string
            - 'null'
        domain_id:
          type:
            - string
            - 'null'
          pattern: ^biq_dom_[0-9A-HJKMNP-TV-Z]{26}$
        alias:
          type:
            - string
            - 'null'
        active:
          type: boolean
        has_password:
          type: boolean
        activates_at:
          type:
            - string
            - 'null'
          format: date-time
        expires_at:
          type:
            - string
            - 'null'
          format: date-time
        exp_clicks_rule:
          oneOf:
            - $ref: '#/components/schemas/ClickExpirationRule'
            - type: 'null'
        utm:
          type:
            - string
            - 'null'
        geo_rules:
          type: array
          items:
            $ref: '#/components/schemas/TargetingRule'
        device_rules:
          type: array
          items:
            $ref: '#/components/schemas/TargetingRule'
        platform_rules:
          type: array
          items:
            $ref: '#/components/schemas/TargetingRule'
        folder_ids:
          type: array
          items:
            type: string
        pixel_ids:
          type: array
          items:
            type: string
        tag_ids:
          type: array
          items:
            type: string
        conversion_tracking_enabled:
          type: boolean
        allow_search_engine_indexing:
          type: boolean
        proxy:
          type: boolean
        title:
          type:
            - string
            - 'null'
        description:
          type:
            - string
            - 'null'
        image:
          type:
            - string
            - 'null'
        qr_code:
          oneOf:
            - $ref: '#/components/schemas/AttachedQrCode'
            - type: 'null'
        clicks_count:
          type: integer
          minimum: 0
          description: Total recorded clicks.
        leads_count:
          type: integer
          minimum: 0
          description: Total attributed lead events.
        sales_count:
          type: integer
          minimum: 0
          description: Total attributed sale events.
        revenue:
          type: number
          minimum: 0
          description: Attributed sales amount normalized to USD.
        revenue_currency:
          type: string
          const: USD
        clicked_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Time of the most recent recorded click.
        safety_status:
          type: string
          enum:
            - clear
            - pending
            - quarantined
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ApiError:
      type: object
      required:
        - error
        - request_id
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - invalid_token
                - insufficient_scope
                - upgrade_required
                - quota_exceeded
                - resource_not_found
                - domain_taken
                - alias_taken
                - external_id_taken
                - folder_name_taken
                - tag_name_taken
                - pixel_name_taken
                - biolink_name_taken
                - method_not_allowed
                - url_blocked
                - dns_verification_failed
                - validation_error
                - rate_limit_exceeded
                - internal_server_error
            message:
              type: string
            details:
              type: object
              additionalProperties: true
        request_id:
          type: string
          description: Request identifier to include when contacting support.
    ClickExpirationRule:
      type: object
      additionalProperties: false
      required:
        - key
      properties:
        key:
          type: integer
          minimum: 1
          description: Number of clicks after which the link expires.
        value:
          type:
            - string
            - 'null'
          description: Optional URL to use after the click threshold is reached.
          maxLength: 1000
    TargetingRule:
      type: object
      additionalProperties: false
      required:
        - key
        - value
      properties:
        key:
          type: string
          description: Match value, such as a country code, device, or platform.
          maxLength: 250
        value:
          type: string
          description: Destination URL used when the rule matches.
          maxLength: 1000
    AttachedQrCode:
      type: object
      required:
        - url
        - format
        - logo
      properties:
        url:
          type: string
          format: uri
        format:
          type: string
          const: svg
        logo:
          type: string
          enum:
            - app
            - none
  responses:
    Unauthorized:
      $ref: '#/components/responses/ApiErrorResponse'
      description: The workspace API key is missing, invalid, or revoked.
    Forbidden:
      $ref: '#/components/responses/ApiErrorResponse'
      description: >-
        The key lacks a required scope, the workspace must upgrade, or its quota
        is exhausted.
    NotFound:
      $ref: '#/components/responses/ApiErrorResponse'
      description: >-
        The requested resource or referenced public ID is unavailable in the
        key's workspace.
    Conflict:
      $ref: '#/components/responses/ApiErrorResponse'
      description: >-
        A unique alias, external ID, custom domain, or workspace resource name
        is already in use.
    UnprocessableEntity:
      $ref: '#/components/responses/ApiErrorResponse'
      description: >-
        The payload is invalid, a destination was blocked, or domain DNS
        verification is not ready.
    RateLimited:
      description: The workspace exceeded its plan's API rate limit.
      headers:
        RateLimit-Policy:
          description: Current IETF HTTPAPI quota policy as a structured field.
          schema:
            type: string
            example: '"workspace-api";q=1000;w=60'
        RateLimit:
          description: Current IETF HTTPAPI service limit as a structured field.
          schema:
            type: string
            example: '"workspace-api";r=0;t=17'
        Retry-After:
          description: Seconds to wait before retrying the request.
          schema:
            type: integer
            minimum: 0
            example: 17
        X-RateLimit-Limit:
          description: Legacy maximum request count for the current window.
          schema:
            type: integer
            minimum: 1
            example: 1000
        X-RateLimit-Remaining:
          description: Legacy remaining request count.
          schema:
            type: integer
            minimum: 0
            example: 0
        X-RateLimit-Reset:
          description: Legacy reset time as a UTC Unix timestamp.
          schema:
            type: integer
            format: int64
            example: 1788126519
        X-Biq-Request-Id:
          description: Request identifier for support and tracing.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    InternalError:
      $ref: '#/components/responses/ApiErrorResponse'
      description: The request failed unexpectedly.
    ApiErrorResponse:
      description: API error.
      headers:
        X-Biq-Request-Id:
          description: Request identifier for support and tracing.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Workspace API key
      description: A workspace API key beginning with biqli_.

````