Skip to main content

Setup

upsun integration:add --type=webhook --url=<URL_TO_RECEIVE_JSON>
The webhook URL receives a POST message for every activity that’s triggered. The message contains complete information about the entire state of the project at that time. It’s possible to set the integration to only send certain activity types, or only activities on certain branches. The CLI prompts you to specify which to include or exclude. Leave at the default values to get all events on all environments in a project. For testing purposes, you can generate a URL from a service such as webhook.site and use the generated URL as <URL_TO_RECEIVE_JSON>.

Authentication

You can sign webhook payloads so that your receiving application can verify they genuinely come from Upsun. To do so, provide a shared secret key when creating or updating the integration:
upsun integration:add --type=webhook --url=<URL_TO_RECEIVE_JSON> --shared-key=<SECRET_KEY>
To update the key on an existing integration:
upsun integration:update <INTEGRATION_ID> --shared-key=<SECRET_KEY>
Use a long, random string as the secret key (for example, the output of openssl rand -base64 32).

How it works

When a shared key is configured, every webhook request includes an X-JWS-Signature HTTP header containing a JSON Web Signature (JWS). The webhook body itself remains a standard JSON payload — the signature is separate. The signature uses the following scheme:
  • Algorithm: HS256 (HMAC-SHA256 with the shared key)
  • Format: JWS Compact Serialization with a detached, unencoded payload per RFC 7797
  • JWS protected header: {"alg":"HS256","b64":false,"crit":["b64"]}
Because the payload is detached and unencoded (b64:false), the X-JWS-Signature value has the form:
<base64url-encoded-header>..<base64url-encoded-signature>
Note the two dots with nothing between them — the payload slot is empty because the payload is sent in the POST body rather than embedded in the JWS token.

Verifying the signature

To verify a webhook request:
  1. Read the raw POST body as bytes exactly as received on the wire and preserve it unchanged (this is the JSON payload).
  2. Read the X-JWS-Signature header.
  3. Parse the JWS protected header and signature from the header value (the string in the form <base64url-encoded-header>..<base64url-encoded-signature>). Then, do one of the following:
    • Use a JWS library that supports RFC 7797 detached, unencoded payloads (b64:false) and pass the raw body bytes from step 1 as the detached payload.
    • Manually compute the JWS signing input as <base64url-encoded-header>.<raw-body-bytes> and verify the HS256 MAC over this signing input with your shared key.
  4. Verify the signature using HS256 with your shared key, treating the raw body bytes from step 1 as the payload. Do not try to build a new compact JWS string by inserting the raw body between the two dots.
  5. If verification fails, reject the request (for example, respond with 401).
Signature verification depends on the exact bytes of the HTTP request body. Do not parse and re-serialize the JSON (which can change whitespace or key order) before verifying; always verify against the original raw body bytes as received.

Webhook schema

See the activity script reference for a description of the webhook payload.

Validate the integration

To verify your integration is functioning properly, run the following CLI command:
upsun integration:validate
Last modified on April 7, 2026