How to do it
- Create a GitHub trigger In https://app.socialhandoff.com, open Connections and add a GitHub trigger with a release rule: which networks to draft for and which agent the drafts are attributed to. Copy the trigger id and the signing secret.
- Store the secret In your repository settings, add an Actions secret named ANNOUNCE_WEBHOOK_SECRET with the signing secret.
- Add the action to your repo Copy the socialhandoff-announce-action folder from https://github.com/socialhandoff/socialhandoff into the root of your repository, so the workflow can reference it as a local action.
- Add the workflow Create .github/workflows/announce.yml with the YAML below and set socialhandoff_url to your API origin and trigger_id to your trigger.
- Publish a release Publish a release. The drafts appear in your inbox with the release cited. Approve them to publish.
name: announce
on:
release:
types: [published]
jobs:
announce:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./socialhandoff-announce-action
with:
socialhandoff_url: https://app.socialhandoff.com
trigger_id: YOUR_TRIGGER_ID
webhook_secret: ${{ secrets.ANNOUNCE_WEBHOOK_SECRET }} What the action sends
On a published release, the action builds a small JSON payload: the tag, the release name, the release notes, the release URL, and the repository name. It signs the payload with HMAC-SHA256 using your secret and posts it to /v1/hooks/github/ plus your trigger id, with the same x-hub-signature-256 header GitHub's own webhooks use. SocialHandoff verifies the signature, applies your release rule, and creates drafts attributed to the chosen agent, with the release linked as a citation in the audit log.
Nothing publishes from CI. The drafts wait in the approval inbox, the phone swipe stack, or the MCP approval card in a chat client.
Inputs
socialhandoff_url(required): your SocialHandoff API origin. For the hosted version that is https://app.socialhandoff.com.trigger_id(required): the GitHub trigger id from Connections.webhook_secret(required): the trigger's signing secret, from an Actions secret.tag,name,body,repository(optional): override what is sent. By default the action uses the ref name, the tag as the name, and the repository running the workflow.
The action uses jq, openssl, and curl, which are present on GitHub-hosted Ubuntu runners. On a self-hosted runner, install them first.
Action or plain GitHub webhook?
You can also point a repository webhook straight at the same trigger URL. The action is useful when you want to override the notes (for example, a short summary instead of a long changelog), when releases are created by another workflow, or when you want the call visible in your CI logs.
After the drafts arrive
The release rule decides the networks. A common setup is X and Bluesky for a short note, LinkedIn for a longer post, and Mastodon for the developer audience. Edit inline, approve, and choose publish now (with a 60-second undo) or a scheduled slot. For deeper posts about the release, ask a coding agent such as Claude Code to draft a thread from the diff.
Troubleshooting
- 401 or signature error: the secret in GitHub does not match the trigger's secret. Rotate and update both.
- 404: wrong trigger id, or the URL has a trailing path.
- Workflow did not run: the trigger is
releasewith typepublished; drafts and pre-release edits do not fire it. - No drafts: the release rule may have no networks, or the kill switch is on.
Release posts per network: GitHub Actions for X, GitHub Actions for LinkedIn, GitHub Actions for Bluesky, GitHub Actions for Mastodon, and GitHub Actions for Threads.