How Webhooks Work
Subscription model, authentication, request signing, base URLs, and the two signatures you will encounter.
Goal: Understand the subscription model, configure authentication, and register your first webhook subscription.
Estimated time: 15 minutes
Prerequisites
- A Spare sandbox account with an active
pissubscription - App ID, API key, and EC P-256 private key (registered in the Spare dashboard)
- A publicly reachable HTTPS URL, or a tunnel such as ngrok for local development
How It Works
Spare webhook delivery has three participants: your application registers a subscription, Spare delivers signed events to the URL in that subscription, and your receiver acknowledges each delivery with 202 Accepted.
The usual sequence:
- Call
GET /webhooks/catalogto see which products and resources are available on your tenant. - Call
POST /webhookswith your HTTPS URL, one product, and the resource types you want. - Whenever a matching resource changes state, Spare sends a signed compact JWS to your URL.
- Your receiver reads the raw body, verifies the signature, returns
202 Accepted, and processes the event in the background.
What a Subscription Is
A subscription links one of your URLs to one slice of the event stream. It has four meaningful fields:
| Field | Description |
|---|---|
url | The HTTPS endpoint Spare posts to. Every matching event lands here. |
product | One product per subscription. payments is available today. |
resources | The resource types under that product you want events for. |
status | Active delivers events. Inactive pauses delivery without deleting the subscription. |
A subscription created with { "product": "payments", "resources": ["payment", "consent"] } receives every payment.* and every consent.* event at that URL.
Authentication and Request Headers
Subscription management uses the same API-key session token as the rest of the payments API.
| Header | When | Value |
|---|---|---|
Authorization | Always | Bearer <access_token> |
x-tenant | Always | UAE |
Content-Type | Create and update | application/json |
x-signature | Create and update | Detached ES256 JWS over the request body |
Two Signatures, Two Directions
There are two different signatures in this integration and mixing them up is the most common setup mistake.
x-signature: something you put on yourPOST /webhooksandPATCH /webhooks/{webhookId}requests. It proves the call came from your application and the body was not altered. Spare verifies it before storing your subscription. This is the same mechanism as payment-request signing described on Request Signing.- Event body signature: something Spare puts on every delivery it sends to your URL. The body itself is a compact JWS, not plain JSON. Your receiver verifies this signature before acting on the event.
The two never appear on the same message.
Signing a Create or Update Request
POST /webhooks and PATCH /webhooks/{webhookId} require an x-signature header. The signing process matches payment-request signing:
- Produce the JSON body with fields sorted alphabetically. Omit any
nullor absent fields. - Sign that canonical JSON with your registered EC P-256 private key using ES256.
- Send the signature as a detached JWS in the
x-signatureheader:header..signature(payload segment is empty).
The body allow-list is url, description, status, product, and resources. Any other field is rejected with 400 before the signature is checked.
See Request Signing for key generation, serialization rules, and per-language examples.
Base URLs and Paths
The sandbox base URL for UAE:
https://api.sandbox.tryspare.aeSubscription management lives under /webhooks (plural). The delivery verification key set lives at /webhook/jwks (singular). The two are easy to confuse:
GET /webhooks/catalog # subscription APIs, authenticated
GET /webhook/jwks # delivery verification keys, anonymousUse the path from the relevant section. The difference of one character matters.