ShareNotes
2026-08-02 · Workflow

Share text straight from your terminal

Turn stdout into a URL with one curl command — no API key, no account, no install. Piping patterns, a shell function worth stealing, CI usage, and what you should not send this way.

You have something in a terminal. Someone else needs to see it in a browser. The usual routes are all slightly wrong: pasting a 200-line stack trace into Slack buries the channel, email mangles the whitespace, and a screenshot of text is a small cruelty.

What you actually want is to turn stdout into a URL. Here is the whole thing:

curl -X POST https://sharenotes.dev/api/v1/notes \
  -H "Content-Type: text/plain" \
  --data-binary @error.log

That returns JSON with a link you can paste anywhere:

{"slug":"blue-fox-42","url":"https://sharenotes.dev/blue-fox-42","expiresAt":null,"burnAfterRead":false}

No API key. No account. No SDK to install. The rate limit is 20 notes per day per IP, which is far more than an interactive workflow needs.

Piping, which is the actual point

--data-binary @- reads stdin, so anything that writes to stdout can become a link:

# the diff you want a second opinion on
git diff | curl -X POST https://sharenotes.dev/api/v1/notes \
  -H "Content-Type: text/plain" --data-binary @-

# the last 200 lines of a failing service
journalctl -u myapp -n 200 --no-pager | curl -X POST ... --data-binary @-

# a pod that will not start
kubectl logs deploy/api --tail=500 | curl -X POST ... --data-binary @-

# test output, with colour codes stripped so it stays readable
npm test 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | curl -X POST ... --data-binary @-

Strip ANSI colour before piping. Most test runners and build tools emit escape codes when they think a terminal is attached. They survive the pipe and render as noise. The sed above removes them; many tools also accept a --no-color flag, which is cleaner.

Make it a shell function

Typing the full curl every time gets old. Drop this in your .bashrc or .zshrc:

share() {
  local body
  if [ $# -gt 0 ]; then
    # Arguments given — treat the first as a file path, else as literal text.
    body=$([ -f "$1" ] && cat "$1" || printf '%s' "$*")
  else
    body=$(cat)   # nothing passed, so read stdin
  fi

  printf '%s' "$body" \
    | curl -sS -X POST https://sharenotes.dev/api/v1/notes \
        -H "Content-Type: text/plain" --data-binary @- \
    | grep -o '"url":"[^"]*"' | cut -d'"' -f4
}

Now all three of these work, and print just the URL:

git diff | share
share error.log
share "the wifi password is hunter2"

Pipe it to your clipboard to skip the copy step — | pbcopy on macOS, | xclip -sel clip on Linux, | clip.exe under WSL.

Options worth knowing

Send JSON instead of plain text when you want more than the default:

curl -X POST https://sharenotes.dev/api/v1/notes \
  -H "Content-Type: application/json" \
  -d '{
    "content": "DATABASE_URL=postgres://...",
    "expiresIn": "1h",
    "burnAfterRead": true,
    "title": "staging env"
  }'
FieldWhat it does
expiresIn1h, 24h, 7d or 30d. The note hard-deletes itself after that.
burnAfterReadtrue destroys the note the first time it is read.
customSlug3–10 characters, so you can pick sharenotes.dev/ci-log.
passwordA 4–6 digit PIN, with passwordMode of edit or view_edit.
titleUp to 100 characters, shown in the note view.

Reading one back is a plain GET, which makes it usable as a crude config drop:

curl -s https://sharenotes.dev/api/v1/notes/blue-fox-42 | jq -r .content

Using it from CI

A failing build that posts its own log is a nice trick. In a GitHub Actions step:

- name: Publish failure log
  if: failure()
  run: |
    URL=$(tail -c 100000 build.log \
      | jq -Rs '{content: ., expiresIn: "7d"}' \
      | curl -sS -X POST https://sharenotes.dev/api/v1/notes \
          -H "Content-Type: application/json" --data-binary @- \
      | jq -r .url)
    echo "Build log: $URL" >> $GITHUB_STEP_SUMMARY

Two things to note. jq -Rs wraps arbitrary text as a JSON string, which is the safe way to build the body — hand-rolled string interpolation breaks the moment your log contains a quote or a newline. And tail -c caps the size: the limit is 10MB per note, and a runaway log will blow past that.

CI runners share egress IPs. The 20-notes-per-day limit is per IP, so a busy hosted runner pool can hit it collectively. Post on failure only, rather than on every run.

What not to pipe

The convenience makes it easy to be careless, so, plainly:

  • Notes are not end-to-end encrypted. They are stored server-side. burnAfterRead and short expiries limit how long a copy lingers; they are not a substitute for a secrets manager.
  • Anyone with the URL can open it. The slug is unguessable in practice, but the link is the credential. Add a PIN if that matters.
  • Rotate anything you do send. If a staging token goes into a paste to unblock a colleague, treat it as burnt and cycle it afterwards.
  • Watch what is already in your scrollback. env | share is a very fast way to publish every secret on the machine.

Why a URL beats the alternatives

Chat apps truncate long pastes and destroy indentation. A gist needs a GitHub account, and anonymous gists cannot be deleted by whoever made them. Screenshots are not searchable, not copyable, and not accessible. A plain link with the text intact avoids all three, and if you set an expiry it cleans up after itself.

The full reference — every field, every error code, the rate limits — is on the developer page. If you would rather not use a terminal at all, the editor does the same thing with a paste and a click.

Frequently asked

How do I share terminal output as a link?

Pipe it into a POST request: command | curl -X POST https://sharenotes.dev/api/v1/notes -H "Content-Type: text/plain" --data-binary @-. The response is JSON containing a shareable URL. No API key or account is required, and the limit is 20 notes per day per IP.

Do I need an API key or account?

No. The ShareNotes API is public and unauthenticated — there is no key to request, store or rotate. Abuse is controlled purely by a per-IP rate limit of 20 note creations per day.

How do I make a note from the command line expire?

Send a JSON body with an expiresIn of 1h, 24h, 7d or 30d, or set burnAfterRead to true so the note is destroyed the first time it is read. Both are optional; without them the note is removed 7 days after it was last opened.

What is the maximum size I can pipe in?

A note can be up to 10MB. Cap the input with something like tail -c 100000 when piping logs, since a runaway log will exceed that and the request will be rejected.

Is it safe to pipe secrets through it?

Treat it as convenient, not secure. Notes are stored server-side and are not end-to-end encrypted, and anyone with the URL can open it. Use burnAfterRead plus a short expiry for a credential you are handing over, and rotate it afterwards. Long-lived secrets belong in a secrets manager.

Can I read a note back from the command line?

Yes — it is a plain GET: curl -s https://sharenotes.dev/api/v1/notes/blue-fox-42 | jq -r .content. Notes that are PIN-protected or already burnt will not return content.

No key · No sign-up · Free forever Read the API docs