Send CI build artefacts to a client

Every tagged release builds something a person outside your team needs — an installer, a signed archive, a rendered video. This recipe has GitHub Actions upload it to Yungle and email the client the link, with no browser and no shared drive.

What you need

  • An API key with transfers:write, from Settings → API keys, stored as a repository secret named YUNGLE_API_KEY (Settings → Secrets and variables → Actions).
  • A build step that leaves its output in a known directory — here, dist/.

Transfers work on every plan, so this runs on the free one too, within its per-transfer size.

The workflow

name: Deliver release

on:
  push:
    tags: ['v*']

jobs:
  deliver:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci && npm run build

      - run: npm install -g yungle-cli

      - name: Send the build to the client
        env:
          YUNGLE_API_KEY: ${{ secrets.YUNGLE_API_KEY }}
        run: |
          URL=$(yungle send dist/ \
            --to client@example.com \
            --title "Release ${{ github.ref_name }}" \
            --message "Release ${{ github.ref_name }} is ready." \
            --json | jq -r .url)
          echo "Delivered: $URL" >> "$GITHUB_STEP_SUMMARY"

The CLI reads the key from YUNGLE_API_KEY, so there is no login step. A folder keeps its structure: the client downloads dist/ as it was built.

Why it is shaped like this

  • --json and jq — the result is one JSON object on stdout, and progress goes to stderr, so the pipe stays clean. The link lands in the job summary where the release manager will look.
  • Resumable— if the runner’s connection drops mid-upload, the tus upload continues from the last committed part. A rerun of the job on a fresh runner starts a new transfer, which is what you want.
  • Link only is fine — drop --to and nobody is emailed; the link is still in the summary for you to share. That also spends none of your daily email budget.
Recipients are capped at 10 per transfer, and emailing counts against a daily budget per workspace. See Rate & email limits.

Knowing they got it

There are no webhooks yet, so a follow-up job that cares about receipts polls. The transfer id is in the same JSON as the link:

ID=$(yungle send dist/ --json | jq -r .id)
curl -s https://yungle.co/api/v1/transfers/$ID/downloads \
  -H "Authorization: Bearer $YUNGLE_API_KEY" | jq '.recipients[] | {email, downloaded}'