openstatus logoDashboard

How to Auto-Post Status Updates to X and Bluesky

Problem

When you publish a status report or schedule maintenance, you also want to announce it on X (formerly Twitter) and Bluesky — without copy-pasting the same message into each app every time.

Solution

statuspage-socials-notifier is a small open-source Hono service that receives the openstatus webhook payload and cross-posts each update to your X and Bluesky accounts. You add it as a webhook subscriber on your status page, and it does the rest.

This guide walks through collecting the credentials it needs and deploying it.

Prerequisites

Environment variables

The service is configured entirely through environment variables.

VariableRequiredPurpose
OPENSTATUS_WEBHOOK_TOKENNoShared secret the webhook must send as Authorization: Bearer <token>.
BLUESKY_IDENTIFIERNoYour Bluesky handle or email (e.g. acme.bsky.social).
BLUESKY_APP_PASSWORDNoA Bluesky App Password (not your account password).
X_API_KEYNoX app API key (OAuth 1.0a).
X_API_SECRETNoX app API secret.
X_ACCESS_TOKENNoX user access token.
X_ACCESS_SECRETNoX user access token secret.
POST_ON_STATUSESNoComma-separated statuses to post on (e.g. investigating,resolved). Defaults to all.
POST_MAINTENANCENoSet to true to also post scheduled maintenance.
PORTNoServer port. Defaults to 3000 (ignored on serverless platforms).

Step-by-step guide

1. Generate a webhook token

Pick any sufficiently random string — this is the shared secret between openstatus and your service. It is not required but recommended to keep the endpoint authenticated. For example:

openssl rand -hex 32

Save it as OPENSTATUS_WEBHOOK_TOKEN. You'll reuse the same value when you create the subscriber in step 5.

2. Create a Bluesky App Password

App Passwords let an app post on your behalf without exposing your real password.

  1. In Bluesky, open Settings.
Bluesky settings menu
Bluesky settings menu
  1. Go to Privacy and Security and click App passwords.
Bluesky Privacy and Security settings
Bluesky Privacy and Security settings
  1. Click Add App Password and give it a name (e.g. openstatus-notifier).
Naming a new Bluesky App Password
Naming a new Bluesky App Password
  1. Copy the generated password — Bluesky won't show it again.
The generated Bluesky App Password
The generated Bluesky App Password

Set BLUESKY_IDENTIFIER to your handle (e.g. acme.bsky.social) and BLUESKY_APP_PASSWORD to the value you just copied.

3. Create an X app and access tokens

  1. Open the X Developer Portal and go to Apps.
X Developer Console apps list
X Developer Console apps list
  1. Click Create App, give it a name, and pick an environment.
Creating a new X client application
Creating a new X client application
  1. On creation, X shows your Consumer Key and Secret Key once — copy them to X_API_KEY and X_API_SECRET.
X application created with consumer and secret keys
X application created with consumer and secret keys
  1. Open the app's Keys & Tokens tab. Under User authentication settings, enable OAuth 1.0a with Read and write permissions, then generate an Access Token and Secret.
X app keys and tokens with OAuth 1.0a access token
X app keys and tokens with OAuth 1.0a access token
  1. Copy the Access Token and Access Token Secret to X_ACCESS_TOKEN and X_ACCESS_SECRET — they're shown only once.
X access token and secret generated
X access token and secret generated

Make sure the access token is generated after you set Read and write permissions — otherwise posts will fail with a permissions error.

4. Deploy the service

Fork or clone the repository, then deploy it to your platform of choice.

Vercel

  1. In Vercel, click Add New → Project and import your fork.
  2. Add every variable from the table above under Settings → Environment Variables.
  3. Click Deploy. Your webhook endpoint will be available at https://<your-project>.vercel.app/webhook.

Fly.io

The repo ships a preconfigured fly.toml. Set each variable as a secret, then deploy:

fly secrets set OPENSTATUS_WEBHOOK_TOKEN=... BLUESKY_IDENTIFIER=... BLUESKY_APP_PASSWORD=... \
  X_API_KEY=... X_API_SECRET=... X_ACCESS_TOKEN=... X_ACCESS_SECRET=...
fly deploy

Railway

Import the repo (Railway auto-detects the Dockerfile) and add the environment variables in the project dashboard.

These platforms scale to zero, so the first request after an idle period incurs a ~1–2s cold start. That fits inside openstatus' 5s webhook timeout, and failed deliveries are retried up to 3 times with exponential backoff — so a cold start may delay, but won't drop, the first post.

5. Add the webhook subscriber in openstatus

  1. In the dashboard, open your status page and go to Subscribers.
  2. Add a subscriber with channel Webhook and set the URL to your deployed endpoint, e.g. https://<your-project-url>/webhook.
  3. Add a request header Authorization with value Bearer <OPENSTATUS_WEBHOOK_TOKEN> (the token from step 1).
Adding a webhook subscriber with URL and Authorization header in openstatus
Adding a webhook subscriber with URL and Authorization header in openstatus

Because the destination isn't a Slack or Discord URL, openstatus sends the generic JSON payload, which the notifier knows how to parse.

6. Test it

Use the subscriber's Send test action to verify connectivity — openstatus delivers a type: "test" payload, which the notifier ignores (it only posts real events), so check your service logs for the received request rather than your social accounts.

To verify end-to-end posting, publish a status report on your page. Within a couple of seconds you should see the update appear on both your X and Bluesky accounts.