Documentation Index
Fetch the complete documentation index at: https://docs.linkutm.com/llms.txt
Use this file to discover all available pages before exploring further.
Endpoint
:id is the link’s UUID, not its short code.
Headers
| Header | Required | Notes |
|---|---|---|
Authorization: Bearer <jwt> | Yes (or API key) | A workspace JWT |
Authorization: Bearer lk_live_... | Yes (or JWT) | A workspace API key works equally well |
Content-Type: application/json | Yes |
This endpoint sits behind the
JwtOrApiKeyGuard. Either a JWT or an API key (lk_live_ prefix) authenticates the request - send one of them in the Authorization header. The caller identified by the credential is recorded as the link’s updatedById.x-workspace-id header is not read by this endpoint. The link is located purely by its :id.
Body
Every field is optional. Only the fields you send are changed - omitted fields are left untouched. This is a true PATCH.New destination URL.
Internal label. Not visible to visitors.
Internal context.
Free-form notes on the link.
New short code. Validated server-side: only letters, numbers, hyphens, and underscores (
^[a-zA-Z0-9_-]+$), and 50 characters or fewer. Checked for global uniqueness against every other link - a clash returns 409. Sending an empty string is treated as “no change” and the slug is left as-is.UTM source.
UTM medium.
UTM campaign.
UTM term.
UTM content.
Arbitrary custom UTM keys.
{"audience": "lookalike_3"} becomes &utm_audience=lookalike_3 on redirect. Sending this field replaces the stored object.UUID of a UTM template the link is associated with. Pass
null to detach the link from its template. The value is written directly to the record - the update path does not re-apply template UTM fields, so set the UTM fields explicitly if you want them changed.UUID of the folder to move the link into. Pass
null to unfile the link.Set
true to archive the link, false to restore it. Archived links return 404 on the public redirect and drop out of default list views. See Behavior notes.Full replacement set of tag UUIDs. See How tags are handled - this is a replace, not an append.
Sets or changes the link password. Stored on the record. The public redirect gates on this - see Redirects.
ISO 8601 timestamp. Three behaviors based on what you send - see How expiresAt is parsed.
Planned fallback destination. Empty string and
null both clear it - see How fallbackUrl is parsed.Max clicks. Recorded for reporting; the public redirect does not stop on it.
Destination for iOS visitors. The public redirect does route to this when the visitor’s OS is detected as iOS - see Redirects.
Destination for Android visitors. Same routing behavior as
iosUrl.Country-code to URL map. Stored on the record; the public redirect does not currently route on country.
Override OG title for social previews.
Override OG description.
Override OG image URL.
Override favicon URL.
How expiresAt is parsed
The controller inspectsexpiresAt before passing it to the service:
| You send | Stored value |
|---|---|
null | null - expiry is removed, the link no longer expires |
| A non-empty string | Parsed as a Date and stored |
| An empty or whitespace-only string | undefined - field is omitted, current expiry left unchanged |
| Field absent | Field is omitted, current expiry left unchanged |
"expiresAt": null explicitly. Sending "" does not clear it.
How fallbackUrl is parsed
| You send | Stored value |
|---|---|
null | null |
An empty string "" | null - empty string is normalized to null |
| A non-empty string | Stored as-is |
| Field absent | Field is omitted, current value left unchanged |
null and "" clear fallbackUrl. This differs from expiresAt, where an empty string is a no-op.
How tags are handled
tagIds is a full replacement. When the field is present, the service:
- Deletes every existing
link_tagsrow for this link. - Inserts one row per UUID in the array you sent.
- Returns the link re-fetched with its new tag set.
tagIds before patching.
Example request
Example response
Allid fields are UUIDs. When tagIds is present in the request, the response is the link re-fetched after the tag rows are rewritten, and tags contains { tagId } entries. When tagIds is absent, the response is the updated link with fully expanded tags (each carrying the joined tag object).
Errors
| Code | When |
|---|---|
400 | customSlug contains characters outside [a-zA-Z0-9_-], or is longer than 50 characters |
401 | Missing or invalid JWT / API key |
404 | Link with the given :id does not exist |
409 | customSlug is already in use by another link |
Behavior notes
- This is a partial update. Fields you omit are never written, so you can safely send only the fields you want to change.
- UTM string fields are written directly. The update path does not run the workspace UTM Rules processing that link creation does - the values you send are stored verbatim.
customSlugmaps to the link’sshortCode. Changing it changes every existing short URL for the link, including its QR code target. Old short codes stop resolving immediately.updatedByIdis set to the user resolved from the JWT or API key on every successful update.archivedtoggled here affects only the single link. Workspace link-usage counters are recalculated by the bulk archive endpoint, not by this PATCH - usePOST /links/bulk-archivewhen you need the usage counter kept in sync.expiresAtandarchivedare the two fields enforced by the public redirect. An expired link returns410, an archived link returns404. See Redirects.fallbackUrl,clickLimit, andgeoTargetsare stored but not acted on by the redirect.iosUrlandandroidUrlare stored and are used by the redirect for device-based routing.