Your CI already builds the artifact.
Then someone pastes it into Slack as a 40 MB attachment — or opens a throwaway Google Drive link that expires next week with no signal that anyone opened it.
If the person on the other end is not living in GitHub Releases, you need a handoff link: one URL, browser-readable, optional password/expiry, and a simple “was it opened?” check.
This post walks through a practical pattern:
- Build a ZIP (or PDF) in GitHub Actions
- Upload it with one authenticated
curl - Post the share URL back on the PR (or into Slack)
I use TiniDrop for the share step because that is what we built for this exact loop — but the workflow shape is the same idea wherever you land the file: artifact in CI → link out → humans click a URL, not a binary.
When this beats GitHub Releases (and email)
Use a share link when:
- The recipient is a client, investor, or teammate who is not cloning repos
- You are shipping a finished ZIP / PDF / HTML preview, not ongoing source
- You want a lightweight “did they open it?” signal after the PR merges
- You might want a password or expiry on that one URL
Keep Releases for versioned open-source downloads. Use a link for the human handoff.
What you need
- A GitHub repo with Actions enabled
- A ZIP (or PDF) produced by your job —
dist.zip,site.zip,deck.pdf, etc. - A TiniDrop account on Solo or Pro (API keys live under Dashboard → Settings → API keys)
- An API key stored as a GitHub Actions secret — never in the repo
Generate the key once in the dashboard. Name it something like github-actions-ci. Copy it only at creation time.
Add a repository secret:
- Name:
TINIDROP_API_KEY - Value: the key (starts with
td_if you created it recently)
Minimal upload from your laptop (sanity check)
Before wiring Actions, prove the API from your machine:
curl -sS -X POST https://tinidrop.com/api/upload \
-H "x-api-key: $TINIDROP_API_KEY" \
-F "file=@dist.zip"
A successful response includes a shareable url (and a slug). Shape:
{
"slug": "a8k2mx",
"url": "https://tinidrop.com/view/a8k2mx",
"original_name": "dist.zip",
"size_bytes": 1843200
}
Open that url in a private window. If it loads, you are ready for CI.
Note: API upload is for Solo / Pro. Free-plan uploads go through the browser (bot check). Put the key in CI secrets and rotate it if it ever leaks — same hygiene as any deploy token.
GitHub Actions: upload after build
Here is a job that builds, zips, uploads, and prints the link.
name: Share build as link
on:
workflow_dispatch:
push:
branches: [main]
jobs:
share:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: |
# replace with your real build
mkdir -p dist
echo "<h1>Preview</h1>" > dist/index.html
- name: Zip artifact
run: |
cd dist && zip -r ../dist.zip .
- name: Upload to TiniDrop
id: upload
env:
TINIDROP_API_KEY: ${{ secrets.TINIDROP_API_KEY }}
run: |
set -euo pipefail
RESP=$(curl -sS -X POST https://tinidrop.com/api/upload \
-H "x-api-key: ${TINIDROP_API_KEY}" \
-F "file=@dist.zip")
echo "$RESP" | tee upload.json
URL=$(echo "$RESP" | jq -r '.url')
if [ -z "$URL" ] || [ "$URL" = "null" ]; then
echo "Upload failed — check API key and plan"
exit 1
fi
echo "url=$URL" >> "$GITHUB_OUTPUT"
echo "Share URL: $URL"
- name: Summary
run: |
echo "### Build ready" >> "$GITHUB_STEP_SUMMARY"
echo "[Open share link](${{ steps.upload.outputs.url }})" >> "$GITHUB_STEP_SUMMARY"
What this gives you:
- A green check on
main(or manual dispatch) - A clickable link in the Actions job summary
- No attachment bouncing through email size limits
Bonus: comment the link on the pull request
Useful when reviewers are not watching the Actions tab:
name: PR preview link
on:
pull_request:
types: [opened, synchronize]
jobs:
preview:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Build + zip
run: |
mkdir -p dist && cp -R public/* dist/ || true
# your build here…
cd dist && zip -r ../preview.zip .
- name: Upload
id: upload
env:
TINIDROP_API_KEY: ${{ secrets.TINIDROP_API_KEY }}
run: |
RESP=$(curl -sS -X POST https://tinidrop.com/api/upload \
-H "x-api-key: ${TINIDROP_API_KEY}" \
-F "file=@preview.zip")
URL=$(echo "$RESP" | jq -r '.url')
echo "url=$URL" >> "$GITHUB_OUTPUT"
- name: Comment on PR
uses: actions/github-script@v7
with:
script: |
const url = '${{ steps.upload.outputs.url }}';
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `### Preview ready\n\nOpen the share link (view in browser):\n${url}`
});
Reviewers click once. They do not need a GitHub package download or a local unzip ritual.
PDF decks and client ZIPs
Same pattern, different artifact:
- Pitch / proposal PDF — export in CI or commit the latest PDF, upload, send the link before the call
-
Client deliverable ZIP — zip the folder after
npm run build, upload, paste into email -
HTML prototype — zip a folder that contains
index.html; the share link opens in the browser
After you send it, check whether it was opened, then follow up with a specific next step — not “just checking if you got this.”
Hardening checklist
- [ ] Secret is
TINIDROP_API_KEYin GitHub — never in YAML - [ ] Key is Solo/Pro; rotate if a fork PR could have read it (prefer environment secrets with approval for forks)
- [ ] Fail the job if
.urlis missing — do not silently “succeed” with a broken handoff - [ ] For sensitive rounds, set password and expiry on the link from the dashboard after upload
- [ ] Do not treat a share link as a substitute for Git — source stays in the repo
FAQ
Do recipients need an account?
No. They open the link in a browser.
Is this a replacement for GitHub Releases?
No. Releases win for public versioned downloads. This wins for fast, private-ish handoffs to non-Git audiences.
What file types?
PDF, ZIP, HTML/HTM, and common images. Keep the ZIP for small static previews; keep Git for real collaboration.
Where do I start without CI?
tinidrop.com → drop a file → copy the link. Wire Actions once the manual loop feels right.
I am Santosh, co-founder of TiniDrop (Würzburg). If your team still pastes build ZIPs into Slack as attachments, say what you ship in the comments — CI preview, client ZIP, or deck — and we can compare notes on the handoff.
Top comments (0)