Validations
Webhook signatures need to be verified in order to guarantee that the webhook was sent from our system and was not tampered with via a man in the middle attack.
Public Key
Section titled “Public Key”Pipevest signs outbound webhooks with one server-wide ED25519 key per environment — the same key for every partner and every webhook. There is no per-webhook key: configuring a webhook does not generate one, and you never register a key for us to sign with.
The key is identified by the keyid in the Signature-Input header, which is the same value as the
keyId and the JWKS kid returned by GET /v1/webhooks/key. Fetch the matching public key once,
cache it against the environment’s base url and that key id, and use it to verify every webhook
we send.
Key ids
Section titled “Key ids”A key id names exactly one key. It carries both the environment and the generation of that environment’s signing key:
| Environment | Base URL | Key id |
|---|---|---|
Production |
https://api.pipevest.com |
pipevest-live-ed25519-2 |
Sandbox (UAT) |
https://api.staging.pipevest.com |
pipevest-staging-ed25519-2 |
The trailing integer is the key generation, and it increments whenever that environment’s signing key is replaced. Two properties follow, and your verifier can rely on both:
- A key id never names two different keys. Production and staging never share one, and a generation is never reused.
- A rotation always changes the key id. The new key arrives under a key id you have not seen.
So a verifier that caches by (base url, key id) meets an unknown key id, not a signature that
fails to verify. Treat an unknown key id as the cue to re-fetch GET /v1/webhooks/key from that
same base url, cache the key it returns under the key id it reports, and verify with it. Read key
ids from the header and from that endpoint rather than hardcoding them.
Validate Webhook Message
Section titled “Validate Webhook Message”Retrieve Public Key
Make a GET request to /v1/webhooks/key. Public keys are non-secret,
so this endpoint is unauthenticated — no bearer token, no X-Client-Id, and no request
signature of your own.
curl --request GET \ --url https://api.pipevest.com/v1/webhooks/keyReturns HTTP 200 with the key in both SPKI PEM and JWKS form — use whichever your verification library prefers.
{ "data": { "keyId": "pipevest-live-ed25519-2", "algorithm": "ed25519", "publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA...\n-----END PUBLIC KEY-----\n", "jwks": { "keys": [ { "kty": "OKP", "crv": "Ed25519", "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", "kid": "pipevest-live-ed25519-2", "use": "sig", "alg": "EdDSA" } ] } }}Both production and staging serve a key today, so treat a
503 from this endpoint as an incident to
report and retry, not as a state to design around.
Verify Content Digest
Verify HTTP Message Signature
What Pipevest Signs
Section titled “What Pipevest Signs”Every webhook is a POST with a JSON body, signed under the label sig1. The covered components are
fixed — they do not vary by event, subscription or destination:
| Component | Value |
|---|---|
"content-type" |
Always application/json |
"content-digest" |
sha-512=:<base64>: over the exact bytes of the webhook body |
"content-length" |
Byte length of the body |
"@method" |
Always POST |
"@target-uri" |
The full webhook url you registered |
"@path" |
The path component of that url |
The signature parameters carry keyid and created only. Pipevest does not send expires on
outbound webhooks, so do not reject a webhook for a missing or expired expires — bound replay with
created and the payload’s eventId instead.
Example Headers
Section titled “Example Headers”Content-Type: application/jsonContent-Length: 118Content-Digest: sha-512=:RK/0qy18MlBSVnWgjwz6lZEWjP/lF5HF9bvEF8FabDg=:Signature: sig1=:OTEyMjY4...A5NTNDMEQ=:Signature-Input: sig1=("content-type" "content-digest" "content-length" "@method" "@target-uri" "@path");keyid="pipevest-live-ed25519-2";created=1732893484Example Signature Base
Section titled “Example Signature Base”Rebuilt from the Signature-Input above, for a webhook delivered to https://companyx.com/webhooks:
"content-type": application/json"content-digest": sha-512=:RK/0qy18MlBSVnWgjwz6lZEWjP/lF5HF9bvEF8FabDg=:"content-length": 118"@method": POST"@target-uri": https://companyx.com/webhooks"@path": /webhooks"@signature-params": ("content-type" "content-digest" "content-length" "@method" "@target-uri" "@path");keyid="pipevest-live-ed25519-2";created=1732893484