Open Morse

Guide

Voice notes

These routes are built for a client that owns its own data and syncs it: a phone that records offline and reconciles later. That shapes almost everything about them, starting with who decides the id.

You choose the id

Unlike everything else in this API, you do not POST and receive an id back. You pick one and PUT the note at it. The same call creates or replaces, so a client that recorded something offline can send it whenever it next has a connection, without a round trip first.

PUT/voice-notes/{note_id}

curl -X PUT "$MORSE/voice-notes/$ID" \
  -H "Authorization: Bearer $MORSE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "9d4e7c21-3b6f-4a05-8e92-5c1d0f8a2b64",
    "status": "ready",
    "created_at": "2026-10-02T10:30:00+05:30",
    "title": "Notes after the pricing call",
    "duration_seconds": 94
  }'

It replaces the whole note. To change part of one, use PATCH, which leaves the fields you do not send alone.

Not clobbering an edit

Two devices syncing the same note will eventually both write it. Send If-Unmodified-Since with the time you last saw, and the write is refused if someone got there first, rather than silently overwriting them.

PATCH/voice-notes/{note_id}

curl -X PATCH "$MORSE/voice-notes/$ID" \
  -H "Authorization: Bearer $MORSE_TOKEN" \
  -H "Content-Type: application/json" \
  -H "If-Unmodified-Since: Wed, 02 Oct 2026 05:00:00 GMT" \
  -d '{ "title": "Pricing call, follow-ups" }'

Listing and paging

Newest first, a page at a time, with a cursor rather than an offset.

GET/voice-notes

curl "$MORSE/voice-notes?limit=20&status=ready" \
  -H "Authorization: Bearer $MORSE_TOKEN"
ParameterWhat it doesNote
limitHow many to return.A page at a time, newest first.
cursorWhere the last page stopped.Absent on the first call.
sinceOnly what changed after this time.For catching up, not for browsing.
statusNarrow to some states.Repeatable.
qSearch the text.
include_deletedInclude soft-deleted notes.Needed to learn a note was deleted.

For syncing rather than browsing, ask only for what changed, and include the deleted ones, or your client will never learn that a note went away.

curl
curl "$MORSE/voice-notes?since=2026-10-02T05:00:00Z&include_deleted=true" \
  -H "Authorization: Bearer $MORSE_TOKEN"

The audio

The recording is uploaded separately from the note, in the same three steps as everything else, with one addition: you declare a sha256, so Morse can tell whether what arrived is what you sent. There is also an abort call, for giving up cleanly on a transfer that will not finish.

GET /voice-notes/{id}/audio redirects to a short-lived link to play it back. The full sequence is in Uploads.

Keeping in step

Changes stream as server-sent events. There is no replay buffer: every connection opens with a resync, which is the server telling you to go and fetch what you missed rather than replaying it.

GET/voice-notes/events

curl -N "$MORSE/voice-notes/events" \
  -H "Authorization: Bearer $MORSE_TOKEN"

So the pattern is: open the stream, take the resync as a cue to call GET /voice-notes?since=…, then follow the events until the connection drops, at which point you do it again.

Deleting

  • Soft by default. A deleted note is restorable for thirty days, then erased by a sweep.
  • 410, not 404. Fetching one that has been deleted tells you it existed and is gone, which is what a syncing client needs to hear.

Speakers

Morse keeps a voiceprint per known speaker so it can name who is talking. These are yours: GET /voice-notes/speakers lists them, PUT /speakers/{id} saves one, and DELETE /speakers/{id} forgets one.

A note can also be pulled out as Markdown or plain text: GET /voice-notes/{id}/export?format=markdown. Full schemas are under Reference → Voice-notes.