Link Actions
The link family creates a tracked short link while a workflow is running, and hands its address to the next step. A campaign link for a product that did not exist at deploy time cannot be declared in configuration; this is how one gets made. Three operators.
| Operator | Props | Description |
|---|---|---|
create |
slug, destination, title?, tags?, notes?, utm? |
Mint a link and return its address. |
update |
slug, plus any of destination, title, tags, notes, utm |
Re-point or re-label an existing link. Omitted props are left unchanged. |
delete |
slug |
Retire a link. Soft — the click history survives. |
# Announce a product the moment it is created
- name: mint
type: link
operator: create
props:
slug: 'product-{{trigger.record.handle}}'
destination: 'https://example.com/products/{{trigger.record.handle}}'
title: '{{trigger.record.name}} — launch'
utm: { source: newsletter, medium: email, campaign: launch }
# Send it
- name: announce
type: email
operator: send
props:
to: '{{trigger.record.owner_email}}'
subject: 'Your link is live'
body: '{{steps.mint.result.shortUrl}}'The Address a Later Step Receives
create and update return four fields; delete returns slug and changed.
| Field | Value |
|---|---|
slug |
The resolved slug, after templates. |
destination |
Where the link now points. |
shortUrl |
The absolute address, e.g. https://app.example.com/l/product-atlas. |
qrUrl |
The absolute QR code, e.g. https://app.example.com/l/product-atlas.svg. |
Both URLs are absolute, and that is the point. They are minted to be pasted into an email, an SMS or a PDF, where a root-relative /l/product-atlas has no page to resolve against and is simply a dead string. The origin comes from BASE_URL when you set it, otherwise from the address the server actually bound to, and only then from localhost and the port. Set BASE_URL in production — it is what the link a customer clicks will carry.
Templates Are Resolved First, Then Checked
Every string prop is a template. slug, destination, title, notes and each entry of tags and utm are interpolated against the trigger and earlier steps, and the resolved value is what gets validated.
That ordering is what makes the action safe to template freely. A slug is checked against the slug rules after {{trigger.record.handle}} has become atlas, so a record whose handle is Not A Slug! fails the step instead of storing an address nobody can reach. A destination is checked the same way: the resolved value must be a root-relative path or an absolute http(s) URL, so a javascript: or data: value arriving through a record field is refused rather than minted.
Two Slugs an Automation May Never Take
Both refusals fail the step with the reason named, so a failed run says which of the two happened.
| Refusal | Meaning |
|---|---|
LINK_IS_CONFIG_DECLARED |
The slug is declared in links[]. Edit the configuration file. |
LINK_SLUG_TAKEN |
A live link already holds it. Pick another slug. |
The first is the important one. Configuration is the source of truth for the links it declares, and an automation that could mint over one would be config mutation through a data-shaped side door. update and delete refuse a config-declared slug for the same reason — they can only ever touch links created at runtime.
LINK_SLUG_TAKEN is a refusal, not an overwrite: minting the same slug twice keeps the first link. Re-pointing an address people have already shared, and re-attributing the clicks it has already earned, is not something a repeated trigger should do by accident. Use update when re-pointing is what you mean.
Add continueOnError: true to the step if a refused mint should not stop the workflow.
Delete Keeps the History
delete is soft. The link stops resolving and answers 404, and every click it already earned stays in analytics, reported under the same slug. A hard delete would destroy the campaign record along with the link.
Deleting twice is a success, not a failure — the second call reports changed: false, so a workflow that runs again does not break. A retired slug can be minted again later.
There is no rename. The slug is the address every share already carries and the key every click is recorded under, so changing it would break the shares and orphan the history in one move. Mint a new link instead.
Related Pages
- Short Links — declaring links in configuration, and how they resolve.
- Actions Overview — the action model and the base props every action accepts.
- Analytics — where the clicks these links earn are reported.
- Email & Notifications — the step that usually sends the minted address.
Last updated September 1, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.