Webhook endpoint documentation for claim status change notifications. This endpoint represents the structure of webhook payloads sent to your configured webhook URL when claim statuses change.
Webhook requests
Authentication and Processing
Webhooks sent from Extend require digital signature verification. Once verified, the webhooks can be processed for business logic.
- The webhook payload is JSON.
- The signature is sent as a
signature
header. - Extend's public key id is sent as the
X-Extend-Key-Id
header. - Extend's key id is also sent in the webhook request payload as
kid
. Confirm that the value ofX-Extend-Key-Id
andkid
match.
As part of the verification flow, you will need to make an HTTP GET request to Extend's JWKS endpoint: https://api.helloextend.com/service-orders/oauth/keys.
Request
GET https://api.helloextend.com/service-orders/oauth/keys
Example Response
{
"keys": [
{
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"key_ops": [
"sign",
"verify"
],
"kid": "b35edb9f-4be9-4cb4-9425-6f3d04ac9347",
"n": "tvFPcO-lSUcW9n_dv45wfY0yF47CXZLICqS5kWIypfpP2NX4WRDK8c0bXx6g9P4n_UjJqyDsa-ocMil2dWRDn2oGCzrZLvk6bwerQfCij0Qvy92YZAWJ8r2QrQN69LZzrzCqpMbleclb4du5L46tIdWpOuWx9JWOD_F_HROyd26-SIUvrsLnk7pjSsRx1Bp9mw-qjIB2tEFCINrD3IDcFicPKMpWImhNA-Bukq3ZySzq2VvqAqmhif5EsLw4XWXOucM7YWLn5MfscRippwky94emVD4Hc3LaIYdjxcSMd9Gyr4vgEQU-sgj_hrBfjCabLEsfELIbrC4aPaNN1VTar91ErycEtmH5fNAhI7ka1YUFt5-AgvXDcPs4vnHuTX86TG_0IMhSxr-jvdarYJbTdbLU2-ViRSjIc3FSg4qjvxmyfJNXkwIWHy5Mh3kEK3GA573GofyCTjo20IwjNwhys0PdMnC9YXJQATPE4BvADaaA4UiKDeMfpOXA4Y6dgA9HDRVoA0RgNAycfHh0eIyjnOZjaU7napAxQjr7xqTw_8d8_MpCdAaAb70iL5pbbAtXpuhB27fvKNgvQGD3-X6idEbMYuJmZHzqDMfEmJROd4SjQl0OR5Lay5KpZyvjVsskh0GEbU_HR87UYP8FJn-avDhG6jU-HtSDJahACXeaS-s",
"e": "AQAB"
}
]
}
- Parse the JSON response which contains an array of keys in JWK format. Find the specific key that matches the
kid
from the webhook payload. - Convert the JWK (JSON Web Key) to PEM format, which is required for most signature verification libraries. This typically involves using a crypto library to create a public key object from the JWK and exporting it in PEM format. Here is an example using the
crypto
Node module.
/**
* Convert a JWK to PEM format
*
* @param jwk - The JWK object (the full key Object from the 'keys' array)
* @returns PEM formatted key
*/
function jwkToPem(jwk: any): string {
try {
const publicKey = crypto.createPublicKey({
key: jwk as unknown as NodeJsonWebKey,
format: 'jwk',
})
return publicKey.export({ type: 'spki', format: 'pem' }).toString()
} catch (error) {
Log.error('Error converting JWK to PEM', error)
throw error
}
}
Verify the Digital Signature. Use a cryptographic library to verify that the signature was created using the private key corresponding to the public key. The verification should use the RSA-SHA256 algorithm. The signature is typically base64-encoded and needs to be decoded before verification.
import crypto from 'crypto'
/**
* Returns true if signature verification passed
*
* @param body - Stringified body of the webhook
* @param publicKey - Base64 encoded publicKey.
* @param signature - Base64 encoded signature. Found in webhook ['signature'] header.
* The signature found in the request header is Base64 encoded.
* @returns true if the verification passed
*/
function verifyDigitalSignature(
body: string,
publicKey: string,
signature: string,
): boolean {
try {
return crypto.verify(
'RSA-SHA256',
Buffer.from(body),
Buffer.from(publicKey, 'base64'),
Buffer.from(signature, 'base64'),
)
} catch (e) {
return false
}
}
// Example usage
const publicKey = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUF0dkZQY08rbFNVY1c5bi9kdjQ1dwpmWTB5RjQ3Q1haTElDcVM1a1dJeXBmcFAyTlg0V1JESzhjMGJYeDZnOVA0bi9VakpxeURzYStvY01pbDJkV1JECm4yb0dDenJaTHZrNmJ3ZXJRZkNpajBRdnk5MllaQVdKOHIyUXJRTjY5TFp6cnpDcXBNYmxlY2xiNGR1NUw0NnQKSWRXcE91V3g5SldPRC9GL0hST3lkMjYrU0lVdnJzTG5rN3BqU3NSeDFCcDltdytxaklCMnRFRkNJTnJEM0lEYwpGaWNQS01wV0ltaE5BK0J1a3EzWnlTenEyVnZxQXFtaGlmNUVzTHc0WFdYT3VjTTdZV0xuNU1mc2NSaXBwd2t5Cjk0ZW1WRDRIYzNMYUlZZGp4Y1NNZDlHeXI0dmdFUVUrc2dqL2hyQmZqQ2FiTEVzZkVMSWJyQzRhUGFOTjFWVGEKcjkxRXJ5Y0V0bUg1Zk5BaEk3a2ExWVVGdDUrQWd2WERjUHM0dm5IdVRYODZURy8wSU1oU3hyK2p2ZGFyWUpiVApkYkxVMitWaVJTakljM0ZTZzRxanZ4bXlmSk5Ya3dJV0h5NU1oM2tFSzNHQTU3M0dvZnlDVGpvMjBJd2pOd2h5CnMwUGRNbkM5WVhKUUFUUEU0QnZBRGFhQTRVaUtEZU1mcE9YQTRZNmRnQTlIRFJWb0EwUmdOQXljZkhoMGVJeWoKbk9aamFVN25hcEF4UWpyN3hxVHcvOGQ4L01wQ2RBYUFiNzBpTDVwYmJBdFhwdWhCMjdmdktOZ3ZRR0QzK1g2aQpkRWJNWXVKbVpIenFETWZFbUpST2Q0U2pRbDBPUjVMYXk1S3BaeXZqVnNza2gwR0ViVS9IUjg3VVlQOEZKbithCnZEaEc2alUrSHRTREphaEFDWGVhUytzQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ==';
const requestBody = '{"serviceOrderId":"73752710-8cdf-4fe3-8327-6936fa3badd6","claimId":"7b67180d-0ae6-4d81-a0ca-d7eeda41870e","contractId":"770e5f9d-fb2d-4c53-9137-cbb647d037f5","servicerId":"26db19a3-d9db-4ad4-9575-b7ca2f6e990a","customerName":"Paul Name","customerEmail":"[email protected]","contractPurchaseDate":1655668093818,"customerShippingAddress":{"city":"San Francisco","address1":"533 Mission Street","provinceCode":"CA","countryCode":"USA","postalCode":"94105"},"customerPhoneNumber":"555-535-5555","productDetails":[{"productReferenceId":"PRODUCT-REPAIR-385UmuLNaUqVmpohrmnvMqrncttQCz","productListPrice":25199,"productPurchasePrice":25199}],"failureType":"webhook parse","failureDescription":"webhook","incident":{"failureType":"electricalFailure","description":"BROKE PLZ HALP","occurredAt":1695152893819,"FailureType":"webhook parse","FailureDescription":"webhook","productCondition":"nonfunctional"},"sendDate":1695152952396,"url":"https://webhook.site/70a09585-f934-4b01-855b-0869e5e19752","transactionId":"63815176-f685-44ca-8180-fb13e39d5d04","type":"assign"}';
const signature = 'JJbRyjmmylP4GJtNCT7wANYVMkmUYENuX13sXqMjbAUcfidBBix/hzCjnoycg6lCT1Ktny2OnmixEHQAwNFOuReTlid+AFvCQnQ0nA+aUjvD3JSYCLLE2SOuvbZ7/Q/fQZNnFrDUX6MXcYkuoNLUmKRagG5uq0UyIEtWgfyLtGJQlZ91KXJkvZsFQu+AO5dFiDceJA+IW11sCN5e/9gKWuHxzxwKepvA8S0lyPwQWYqk1V/O/bV5s5uD5zrI6Wndd8Yq7ejYsWd6ZeHbjrcTbbr35lKsTcpoLxtBjD+H/1X5+pYAPlW7ymLWpPxEAug8tnpzy5HVMtwNZ3br8S5afC9BgR7abGcfa3PjyYvM1tk6+JfCPLa9gIJShu1+eXTq0GUQnxu/0bL/ihnwadHlFZyLVzmRjBkPrsuQsM6KTvXS5Gw+BGo+zy/p7iFUFHws1r6zZNONe0ZxTudpDgGN7IoXmvyj7OOJ7VyGROggB0Ptu2iiNz81uTT1M9z+QCR2pa9E+a6fTWTZIXP86acb8HuKDb8Rd9wkU4G609aZc3QHX0ANKIFSWqhz9WDh0XZgT8daMuda+TmtI4NkTS7j9yQLqvbYQ1W2RpCKVBLgXCgiylBoHfRWFZ/dlOdkojBWaFnWa8ytU69IHFgVAd4V4RVPgjNyWgwovtQtiAvpT3o=';
if (verifyDigitalSignature($signature, $publicKey, $requestBody)) {
console.log("Signature is valid!")
} else {
console.log("Signature is not valid.")
}
<?php
// Signature is base64 encoded and found in the 'signature' header on the request
// Public Key returned from Extend API as base64 encoded string
// Request body. Stringified JSON body sent in the webhook request
function verifyDigitalSignature($signature, $publicKey, $requestBody)
{
// Load the public key
$publicKeyResource = openssl_pkey_get_public(base64_decode($publicKey));
if ($publicKeyResource === false) {
return false; // Failed to load the public key
}
// Verify the signature
$result = openssl_verify($requestBody, base64_decode($signature), $publicKeyResource, OPENSSL_ALGO_SHA256);
return ($result === 1); // 1 means the signature is valid
}
// Example usage
$publicKey = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUF0dkZQY08rbFNVY1c5bi9kdjQ1dwpmWTB5RjQ3Q1haTElDcVM1a1dJeXBmcFAyTlg0V1JESzhjMGJYeDZnOVA0bi9VakpxeURzYStvY01pbDJkV1JECm4yb0dDenJaTHZrNmJ3ZXJRZkNpajBRdnk5MllaQVdKOHIyUXJRTjY5TFp6cnpDcXBNYmxlY2xiNGR1NUw0NnQKSWRXcE91V3g5SldPRC9GL0hST3lkMjYrU0lVdnJzTG5rN3BqU3NSeDFCcDltdytxaklCMnRFRkNJTnJEM0lEYwpGaWNQS01wV0ltaE5BK0J1a3EzWnlTenEyVnZxQXFtaGlmNUVzTHc0WFdYT3VjTTdZV0xuNU1mc2NSaXBwd2t5Cjk0ZW1WRDRIYzNMYUlZZGp4Y1NNZDlHeXI0dmdFUVUrc2dqL2hyQmZqQ2FiTEVzZkVMSWJyQzRhUGFOTjFWVGEKcjkxRXJ5Y0V0bUg1Zk5BaEk3a2ExWVVGdDUrQWd2WERjUHM0dm5IdVRYODZURy8wSU1oU3hyK2p2ZGFyWUpiVApkYkxVMitWaVJTakljM0ZTZzRxanZ4bXlmSk5Ya3dJV0h5NU1oM2tFSzNHQTU3M0dvZnlDVGpvMjBJd2pOd2h5CnMwUGRNbkM5WVhKUUFUUEU0QnZBRGFhQTRVaUtEZU1mcE9YQTRZNmRnQTlIRFJWb0EwUmdOQXljZkhoMGVJeWoKbk9aamFVN25hcEF4UWpyN3hxVHcvOGQ4L01wQ2RBYUFiNzBpTDVwYmJBdFhwdWhCMjdmdktOZ3ZRR0QzK1g2aQpkRWJNWXVKbVpIenFETWZFbUpST2Q0U2pRbDBPUjVMYXk1S3BaeXZqVnNza2gwR0ViVS9IUjg3VVlQOEZKbithCnZEaEc2alUrSHRTREphaEFDWGVhUytzQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ==';
$requestBody = '{"serviceOrderId":"73752710-8cdf-4fe3-8327-6936fa3badd6","claimId":"7b67180d-0ae6-4d81-a0ca-d7eeda41870e","contractId":"770e5f9d-fb2d-4c53-9137-cbb647d037f5","servicerId":"26db19a3-d9db-4ad4-9575-b7ca2f6e990a","customerName":"Paul Name","customerEmail":"[email protected]","contractPurchaseDate":1655668093818,"customerShippingAddress":{"city":"San Francisco","address1":"533 Mission Street","provinceCode":"CA","countryCode":"USA","postalCode":"94105"},"customerPhoneNumber":"555-535-5555","productDetails":[{"productReferenceId":"PRODUCT-REPAIR-385UmuLNaUqVmpohrmnvMqrncttQCz","productListPrice":25199,"productPurchasePrice":25199}],"failureType":"webhook parse","failureDescription":"webhook","incident":{"failureType":"electricalFailure","description":"BROKE PLZ HALP","occurredAt":1695152893819,"FailureType":"webhook parse","FailureDescription":"webhook","productCondition":"nonfunctional"},"sendDate":1695152952396,"url":"https://webhook.site/70a09585-f934-4b01-855b-0869e5e19752","transactionId":"63815176-f685-44ca-8180-fb13e39d5d04","type":"assign"}';
$signature = 'JJbRyjmmylP4GJtNCT7wANYVMkmUYENuX13sXqMjbAUcfidBBix/hzCjnoycg6lCT1Ktny2OnmixEHQAwNFOuReTlid+AFvCQnQ0nA+aUjvD3JSYCLLE2SOuvbZ7/Q/fQZNnFrDUX6MXcYkuoNLUmKRagG5uq0UyIEtWgfyLtGJQlZ91KXJkvZsFQu+AO5dFiDceJA+IW11sCN5e/9gKWuHxzxwKepvA8S0lyPwQWYqk1V/O/bV5s5uD5zrI6Wndd8Yq7ejYsWd6ZeHbjrcTbbr35lKsTcpoLxtBjD+H/1X5+pYAPlW7ymLWpPxEAug8tnpzy5HVMtwNZ3br8S5afC9BgR7abGcfa3PjyYvM1tk6+JfCPLa9gIJShu1+eXTq0GUQnxu/0bL/ihnwadHlFZyLVzmRjBkPrsuQsM6KTvXS5Gw+BGo+zy/p7iFUFHws1r6zZNONe0ZxTudpDgGN7IoXmvyj7OOJ7VyGROggB0Ptu2iiNz81uTT1M9z+QCR2pa9E+a6fTWTZIXP86acb8HuKDb8Rd9wkU4G609aZc3QHX0ANKIFSWqhz9WDh0XZgT8daMuda+TmtI4NkTS7j9yQLqvbYQ1W2RpCKVBLgXCgiylBoHfRWFZ/dlOdkojBWaFnWa8ytU69IHFgVAd4V4RVPgjNyWgwovtQtiAvpT3o=';
if (verifyDigitalSignature($signature, $publicKey, $requestBody)) {
echo "Signature is valid!";
} else {
echo "Signature is not valid.";
}
?>
If the signature verification succeeds; process the webhook. If verification fails; reject the webhook.
Webhook Request Body
The webhook request body is the same shape for all of the following supported claims events:
approved: 'claim_approved', denied: 'claim_denied', review: 'claim_in_review',
Events can be subscribed to on an individual basis.
export interface ClaimWebhookData {
// Contract and order identifiers
contractId?: string
orderId?: string
claimId: string
// Claim details
claimType: ClaimType
claimStatus: ClaimStatus
transactionId: string
remainingLimitOfLiability?: number
validationReason?: ClaimValidationReason
coverageTerm?: {
startDate: number
endDate: number
}
// Customer and product information
customer: ClaimCustomerInformation
products: ClaimProduct[]
incident: ClaimFailureInformation
reportedAt: number
// Webhook metadata
sendDate: number
type: string
url: string
kid: string
// Optional fields
denialReason?: ClaimDenialReason
}
Approved Example
{
"contractId": "51d1699c-c8f4-4e50-893b-16af8adc994a",
"orderId": "e18c4150-2bcb-4d80-94af-816f9c59e0e1",
"claimId": "fc47a847-27c8-48c3-a564-68300f5f625b",
"claimType": "extended_warranty",
"claimStatus": "approved",
"transactionId": "6c7278d7-37a5-4253-ba8d-3d266bc68b2a",
"remainingLimitOfLiability": 2000,
"validationReason": "shipping_delivery_waiting_period",
"coverageTerm": {
"startDate": 1593533340000,
"endDate": 1593993600000,
},
"customer": {
"name": "John Doe",
"phone": "111-111-1111",
"email": "[email protected]",
"shippingAddress": {
"address1": "1 Main St",
"address2": "Ste 1",
"city": "San Francisco",
"provinceCode": "CA",
"countryCode": "US",
"postalCode": "11111",
}
},
"products": [{
"referenceId": "referenceId",
"lineItemId": "05f40503-33c4-42d8-9441-e060f34b6002",
"limitOfLiability": {
"amount": 2000,
"currencyCode": "USD",
},
"title": "black t-shirt",
"components": [{
"referenceId": "c0cbe99f-c548-4395-8176-7cb7ab91ab8b",
"productReferenceId": "referenceId",
"value": {
"amount": 2000,
"currencyCode": "USD",
},
"title": "black t-shirt",
}]
}],
"incident": {
"FailureType": "shipment_lost",
"FailureDescription": "The package has been rerouted to the correct destination.",
"Shipment": "1Z57E4700306164050",
"IncidentDate": 1756699200000,
"ContractId": "762b3bcd-4689-4a02-a609-d1678dba96d5",
"LineItemIds": ["3455277d-e8bb-47b2-a4cf-825588884de8"],
"AdjudicationCategory": "shipping_protection",
},
"reportedAt": 1740766088101,
"sendDate": 1740766088101,
"type": "claim_approved",
"url": "https://servicer-webhook-url/post-webhook",
"kid": "b35edb9f-4be9-4cb4-9425-6f3d04ac9347",
}
Denied Example
{
"contractId": "51d1699c-c8f4-4e50-893b-16af8adc994a",
"orderId": "e18c4150-2bcb-4d80-94af-816f9c59e0e1",
"claimId": "fc47a847-27c8-48c3-a564-68300f5f625b",
"claimType": "extended_warranty",
"claimStatus": "denied",
"transactionId": "6c7278d7-37a5-4253-ba8d-3d266bc68b2a",
"remainingLimitOfLiability": 2000,
"validationReason": "mfr_warranty_active",
"coverageTerm": {
"startDate": 1593533340000,
"endDate": 1593993600000,
},
"customer": {
"name": "John Doe",
"phone": "111-111-1111",
"email": "[email protected]",
"shippingAddress": {
"address1": "1 Main St",
"address2": "Ste 1",
"city": "San Francisco",
"provinceCode": "CA",
"countryCode": "US",
"postalCode": "11111",
}
},
"products": [{
"referenceId": "referenceId",
"lineItemId": "05f40503-33c4-42d8-9441-e060f34b6002",
"limitOfLiability": {
"amount": 2000,
"currencyCode": "USD",
},
"title": "black t-shirt",
"components": [{
"referenceId": "c0cbe99f-c548-4395-8176-7cb7ab91ab8b",
"productReferenceId": "referenceId",
"value": {
"amount": 2000,
"currencyCode": "USD",
},
"title": "black t-shirt",
}]
}],
"incident": {
"FailureType": "failure_electrical",
"FailureDescription": "a",
"IncidentDate": 1756699200000,
"ContractId": "762b3bcd-4689-4a02-a609-d1678dba96d5",
"LineItemIds": ["3455277d-e8bb-47b2-a4cf-825588884de8"],
"AdjudicationCategory": "consumer_electronics",
"FailureCause": "weather"
},
"reportedAt": 1740766088101,
"sendDate": 1740766088101,
"type": "claim_denied",
"url": "https://servicer-webhook-url/post-webhook",
"kid": "b35edb9f-4be9-4cb4-9425-6f3d04ac9347",
"denialReason": "under_manufacturer_warranty",
}
Webhooks should be responded to with a status code 200. All requests that do not receive a 200 will be retried every hour for 48 hours.