# Do link previews burn one-time secret links?

> Yes, on services that reveal a secret on page load. What Slack and Safe Links fetch, and why link previews don't burn one-time links on Evergist.

Source: https://evergist.com/blog/do-link-previews-burn-one-time-links/
Published: 2026-09-30

**Short answer:** yes, on any service that reveals and deletes the secret when the URL is loaded. Chat apps and mail scanners load URLs before the recipient does, and that load can be the one allowed view. Evergist links don't work that way. A plain fetch gets a static page, the key after `#` is never part of the request, and a view-limited note uses a view only when the reader presses **Open the note**.

## Why does a link preview use up a one-time link?

Because the service can't tell a preview bot from a person. If the server counts a view when it receives `GET /secret/abc123`, then anything that sends that request spends the view: a chat app building a preview card, a mail gateway scanning for phishing, or a colleague's agent checking that the link works.

The recipient then clicks and sees "this note was already read", and nobody can say whether a bot or a stranger got there first.

## What do Slack, Outlook, and iMessage fetch?

Here is what each vendor's own documentation says. We quote the docs and did not test the fetchers ourselves.

| Fetcher | What the vendor documents | Source |
|---|---|---|
| Slack link unfurling | "Our servers must fetch every URL in a message to determine what kind of content it references." The `Slackbot-LinkExpanding` robot "fetches as little of the page as it can (using HTTP Range headers) to extract meta tags", caches responses for around 30 minutes, and does not honor `robots.txt`. | [Unfurling links](https://docs.slack.dev/messaging/unfurling-links-in-messages/), [Slack robots](https://api.slack.com/robots) |
| Microsoft Defender Safe Links, email | URLs are "scanned prior to message delivery". URLs without a valid reputation "are detonated asynchronously in the background". Rewritten links are scanned again when the user clicks. | [Safe Links overview](https://learn.microsoft.com/en-us/defender-office-365/safe-links-about) |
| Safe Links, Teams | URLs are "checked against a list of known malicious links when the protected user clicks the link". They aren't rewritten. | Same page |
| Apple Messages | Messages shows inline link previews by default. "Link previews do not follow `meta` redirects, nor run JavaScript". Server-side redirects are followed. | [TN3156](https://developer.apple.com/documentation/technotes/tn3156-create-rich-previews-for-messages) |

Microsoft's Teams developer docs say Teams "unfurls a preview" for any link, but they don't describe the fetcher, so we make no claim about it.

Slack and Safe Links both request the URL before the recipient clicks it. Microsoft doesn't say what "detonated" involves, so assume it can behave like a browser.

## What does a plain fetch of an Evergist link return?

The viewer page, which is the same static HTML for every note. The Worker route for `/g/:id` serves that file and never looks the note up, so the request can't touch the view counter.

```sh
# Returns the static viewer page. No view is used.
curl -s https://evergist.com/g/7mQx2Ld9KpTa | head -n 5

# Fetchers that ask for markdown or plain text get a short instruction page.
curl -s -H 'accept: text/markdown' https://evergist.com/g/7mQx2Ld9KpTa
```

The note itself comes from a different endpoint, `GET /api/v1/gists/{id}`, and that request needs a bearer token derived from the key in the fragment. Without the token the API answers 404, exactly as it does for a note that doesn't exist. A fetcher that only knows the path can't spend a view.

The fragment is the part that matters here. HTTP clients don't send anything after `#` to the server, so a preview bot's request carries the note ID and nothing else. [URL fragment encryption keys explained](https://evergist.com/blog/url-fragment-encryption-key-explained/) covers that mechanism and where it leaks.

## Is a view spent before the reader clicks?

Not for a view-limited or password-protected note. When a browser loads the full link, the page script does this:

1. Reads the key from the fragment and derives the access token.
2. Calls `/api/v1/gists/{id}/status`. This returns the expiry, views left, and whether a password is set. It doesn't change the counter.
3. Shows "1 view left. Opening it uses that view." and waits.
4. On **Open the note**, requests the ciphertext. The server decrements the counter in that request, and deletes the note if it reaches zero.

A bot has to run JavaScript, hold the full link with its fragment, and submit the form to spend a view.

## Where can a view still be lost?

We read the code looking for ways this fails and found four.

- **Something that runs the page and clicks.** A scanner or agent that loads the full link in a real browser and presses the button spends the view. Apple documents that Messages previews don't run JavaScript. Slack documents a partial fetch for meta tags. We could not confirm from Microsoft's docs what Safe Links detonation executes.
- **Notes with no view limit and no password open on load.** The viewer decrypts them as soon as the status check returns. There is no view to burn, but a scanner that runs a full browser with the complete link would render the text. The web editor defaults to 1 view. The CLI and MCP server default to no limit, so pass `--views 1` for credentials.
- **A password in the query string.** The viewer accepts `?password=` for scripts. A query string is part of every request, including a preview bot's. Send the password through another channel instead.
- **Anyone holding the full link.** The link is the key. A person or agent who runs `evergist read` on it uses a view, as intended.

## How do I check a note without using a view?

Run `status`. It calls the status endpoint only.

```sh
npx -y evergist status "https://evergist.com/g/<id>#<key>"
```

```text
expires:  2026-09-30T13:00:00.000Z
views:    1 of 1 left
password: no
```

If the note was read, expired, or deleted, `status` reports it as not found. The MCP tool is `gist_status`. Evergist keeps no read log, so it can't tell you who opened a note or when.

## What happens when an AI agent opens the link?

It depends on which tool the agent uses.

A web-fetch tool sends an HTTP request for the URL. The fragment stays behind, so the agent gets the viewer HTML or the markdown instruction page. Both tell the agent that fetching the page doesn't use a view and that the CLI is the way to read the note. Nothing is spent.

`evergist read` and the `read_gist` MCP tool do use a view, and the last view deletes the note.

```sh
# Save first. This read may be the only one.
npx -y evergist read "https://evergist.com/g/<id>#<key>" > note.txt
```

One view and two agents is the easy way to lose a note. An orchestrator runs `read` to confirm the link works, then hands the dead link to a subagent. Use `status` for the check and let the recipient do the read. A browser-automation agent is the other case. It runs the page, has the fragment, and can press the button.

The agent that reads a note does see the value. [How AI agents can share secrets safely](https://evergist.com/blog/how-ai-agents-share-secrets/) covers keeping it out of the transcript, and the [agent guide](https://evergist.com/agents/) lists every tool.

## Questions

### Do Slack link previews burn one-time secrets?

On a service that reveals on page load, they can, because Slack documents that its servers fetch every URL in a message. On Evergist the Slack fetch gets the static viewer page and no view is used.

### Do email link scanners like Safe Links open one-time links?

Microsoft documents that Safe Links scans URLs before delivery and detonates unknown ones in the background. A service that counts a page load as a view can lose the view there. An Evergist view-limited note needs the form submitted in a browser that has the full link, and we haven't tested Safe Links against that.

### Why does my link say the note was already read?

Evergist shows the same message whether the note hit its view limit, expired, was deleted, got 10 wrong passwords, or the key in the link is wrong. The server keeps no read timestamps, so nobody can tell you which. If a credential was in it, treat it as seen and rotate it.

### Is Slack safe for passwords if I use a one-time link?

Safer than pasting the password, because the message history holds a link that stops working after one read. Until then, anyone who can see the message can open it. Add a password and send it somewhere else, as described in [How to share a password or API key securely](https://evergist.com/blog/how-to-share-a-password-securely/).

## Next steps

- Agent instructions, with every command: https://evergist.com/SKILL.md
- MCP server, CLI, and REST API setup: https://evergist.com/agents.md
