Presigned URLs and Permanent Notes Don't Mix
Somewhere in your vault there is probably a note that ends with "recording, see link." It worked the day you pasted it. Click it today and you'll likely get a 403, because the link was never a link. It was a temporary credential.
SpeakPen's Obsidian plugin is built around that fact. When it syncs a voice note into your vault it deliberately leaves the audio URL out and writes a stable speakpen_id into the frontmatter instead. This post explains why, what that costs you, and what it doesn't do.
Why the link in your note dies
Most storage APIs that serve private files, SpeakPen's included, hand out presigned URLs: a normal-looking URL with a signature and an expiry baked into the query string. Think of an S3 presigned GET. For a short window it lets whoever holds it download one object, and then it returns 403. That's not a bug. A presigned URL is meant to be used now and thrown away.
A permanent note is the opposite kind of object. You write it to be read in three years. Put an expiring credential inside it and you have a guaranteed failure on a schedule. It will die, whether or not anything goes wrong.
The worst part is that a dead link looks like evidence. A note that says "recording not saved" makes you go and save it. A note that says "see link" reads as "saved," so the download you meant to do later never happens. The link fails while still looking like it worked.
The usual fallback has its own problem. Drag the .m4a into the vault and the file survives, but you still can't do anything with it. Obsidian can't search inside it, you can't skim it, you can't quote it or link to a sentence in it. It makes every backup heavier, and if you version your vault with git, every clone carries it. The link is light and dies. The file is heavy and survives, but you can't read it. Neither one gives you the content as text.
So the useful question isn't "how do I keep the link alive?" It's "what should the note hold, if any URL I could put there is going to expire?"
What SpeakPen puts in the note instead
Each recording you make in SpeakPen (iOS, Android or speakpen.app/app) becomes a note with a title, a summary, a category and the full transcript. The SpeakPen Sync community plugin writes it into a folder you pick as plain Markdown:
---
speakpen_id: "…"
title: "…"
category: "…"
created_at: …
synced_at: …
---
## Summary
…
## Transcript
…
There is no audio_url field, and that's on purpose. The API does return a presigned audio URL with each note. The plugin reads past it, and the source says why: the URL expires within hours, so writing it down would leave every synced note holding a link that's dead by the next day.
What the note keeps is three things that don't expire:
- The content, as text. Summary and transcript live in the vault. You can search, quote, link and refactor them like anything else you've written. The words don't depend on any server staying up.
-
A stable key.
speakpen_ididentifies the recording in your SpeakPen account. A URL is an address with a timer on it. The id is a name that stays valid as long as the recording exists. When you want to hear the audio again, you open the note in SpeakPen and play it there, and the link is generated at that moment. - Honest absence. The note never claims the audio is one click away, so it can't turn into a tombstone later. It says what was said and how to find the source.
It's a trade, and here is what it costs: listening to the audio takes one extra step, opening SpeakPen, instead of one click. In return the note can't rot. If you mostly re-read voice notes rather than re-listen to them, which is the point of having a transcript, you'll rarely take that step.
The ten-year question, answered honestly
Will the id still find the recording in ten years? Nobody can promise that, and no tool should promise its own servers forever. Look at what each approach is exposed to, though:
- A presigned URL fails on a timer, by design, even when nothing goes wrong.
-
The
speakpen_idstops working only if the recording is deleted or SpeakPen stops existing. That's one specific failure you'd hear about, not a scheduled one. - The note itself is a plain Markdown file. If both Obsidian and SpeakPen disappeared, it would still open in any text editor with the title, summary and transcript intact.
The old way could leave you with nothing but a 403. This way, the worst case loses the audio and keeps the note.
The rest of the plugin follows the same rule
The vault is yours. The plugin only delivers into it. Everything below is in the plugin's source and README:
-
Read-only and single-host. The plugin makes
GETrequests tospeakpen.appand nothing else. It never uploads your vault and never writes back to SpeakPen. It collects no telemetry. - Incremental. Each sync sends a cursor and asks only for notes that changed since the last run. It doesn't sweep the whole account.
- Changes flow in. If a note changes in SpeakPen, the copy in your vault is updated in place.
- Your edits win. The plugin remembers a hash of exactly what it wrote. Once you type into a synced note, the hash stops matching and the plugin leaves that note alone for good. When a later SpeakPen-side change gets skipped because of that, it tells you ("changed in SpeakPen but edited here, so left as-is"). Your words are never overwritten.
- Follows your folder. Change the sync folder and the notes it already wrote move with it.
What it doesn't do
Knowing the limits lets you decide for yourself:
- Sync is one-way. SpeakPen goes into your vault. Edits you make in Obsidian stay in Obsidian and don't go back to SpeakPen.
- No audio in the vault. The plugin doesn't download recordings or embed players. The audio stays in your SpeakPen account.
- The id isn't a clickable link. It's a stable reference, not a shortcut. You still find the recording in SpeakPen.
The same notes, readable by your AI tools
The id-not-URL rule also covers AI access. SpeakPen runs a read-only MCP server that ChatGPT, Claude or a coding agent can connect to (setup at speakpen.app/connect). It has three tools: list recent notes (up to 50), search (up to 10 results) and read one note. It returns text only, never audio, so there are no expiring links for an assistant to quote back to you. It's free on every plan, and you can disconnect it under Settings → Connections.
The three-minute test
- In Obsidian, go to Settings → Community plugins → Browse, search for SpeakPen Sync, install and enable it.
- In SpeakPen, open Settings → Connections. On the Obsidian card, tap Generate token and copy the token right away, because it's shown only once. Paste it into the plugin settings and pick a folder.
- Record one thing: a Control Center, Lock Screen or Action Button press on iOS, "Record with SpeakPen" via Siri, the Quick Settings tile on Android, or speakpen.app/app in a browser. The language is detected automatically.
- Open the note that lands in your vault and look at the frontmatter. You'll find
speakpen_idwhere an audio link would otherwise have been. Then type one sentence into the note, sync again, and check that your sentence is still there.
Free covers 3 recordings a month, up to 5 minutes each. Pro is $9.90/month for 60 recordings a month, up to 20 minutes each. The plugin itself is free.
Then go find that old "see link" note and click it. If it 403s, delete the link and write down what you remember. That way the note at least stops pretending.
Top comments (2)
tr.ee/dev-to
Some comments may only be visible to logged-in visitors. Sign in to view all comments.