Developer recipes

Share a file from your terminal with sh and cURL

Already in a terminal and need a link to a local file? Use a small shell script to upload it with cURL, then capture the URL only when the upload succeeds.

On this page

What this does

The downloadable shell script takes a local file path and a media type, asks the Yoink API for a signed upload URL, PUTs the raw bytes to that URL, and prints the resulting public URL on stdout only after the upload succeeds. It requires a POSIX sh, cURL 7.76 or newer, and jq. Guest requests need no credentials or API key.

Get the script

Download it, then read it before running anything. Do not use curl ... | sh.

sh
work=$(mktemp -d)
cd "$work"
curl --fail-with-body --silent --show-error \
  --connect-timeout 10 --max-time 60 --retry 2 \
  https://yoink.zip/examples/share-file.sh -o share-file.sh
less share-file.sh

Inside you will see that it quotes all paths, uses jq to JSON-encode the filename safely, measures the byte size, applies a 10 second connect timeout and 60 second per-request timeout, retries transient failures at most twice, writes diagnostics to stderr, and emits only the public URL on stdout after a successful PUT.

Run it against a scratch copy

In that scratch directory, make a small public sample before trying your own file:

sh
printf 'Demo release notes\n' > release-notes.txt

For an existing file, substitute its path in the command below. Keep the quotes around paths that contain spaces. For a PNG image, pass image/png as the second argument instead.

Capture the URL safely

Because the script prints the URL only on success, wrap it in a conditional so a failure can never become a chat announcement:

sh
if url=$(sh share-file.sh ./release-notes.txt text/plain); then
    printf 'Shared: %s\n' "$url"
else
    printf 'Upload failed\n' >&2
fi

How the two requests work

The first request sends the filename, media type and measured byte size to Yoink. Its response gives the script a temporary upload URL and the future public URL, but no file has been uploaded yet. The second request sends the file with cURL’s --data-binary option, preserving its contents rather than converting it into a form field. The full field definitions are in the API reference.

There is no multipart form, no JSON wrapper, and no bearer token on the PUT. The signed URL expires after 900 seconds, so retry transient PUT failures against the same URL only while it is still valid; a fresh presign allocates a new path. Treat 400, 401, 403, and 413 as errors to diagnose, not to blindly retry.

After sharing

The public link is public to anyone who has it, with no retention guarantee and no delete API. Keep your original file, and only use the publicUrl after the PUT returns a 2xx status.