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.
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
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
signatureheader. - Extend's public key id is sent as the
X-Extend-Key-Idheader. - Extend's key id is also sent in the webhook request payload as
kid. Confirm that the value ofX-Extend-Key-Idandkidmatch.
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/keysExample 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
kidfrom 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
cryptoNode 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.
{
// Contract and order identifiers
contractId?: string
orderId?: string
claimId: string
// Claim details
claimType: string
claimStatus: string
transactionId: string
remainingLimitOfLiability?: number
validationReason?: string
coverageTerm?: {
startDate: number
endDate: number
}
// Customer and product information
customer: {
name: string
phone?: string
email: string
shippingAddress?: {
address1: string
address2?: string
city: string
provinceCode: string
countryCode: string
postalCode: string
}
}
products: {
referenceId: string
lineItemId?: string
limitOfLiability?: number
title: string
serialNumber?: string
selectedVariant?: {
referenceId?: string
title?: string
imageUrl?: string
}
imageUrl?: string
type?: string
lineItemTransactionId?: string
contractValue?: number
}[]
/** Key-value object with filtered incident/failure details */
incident: Record<string, unknown>
/** Epoch ms when the claim was reported */
reportedAt: number
// Webhook metadata
/** Epoch ms when the webhook was sent */
sendDate: number
type: 'claim_approved' | 'claim_denied' | 'claim_in_review'
url: string
claimDetailsUrl: string
kid: string
// Optional fields
denialReason?: string
customerSegment?: string
riskScore?: number
claimPhotos?: {
required: number
uploaded: number
}
}
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": 2000,
"title": "black t-shirt",
"serialNumber": "SN-2024-XK7892",
"selectedVariant": {
"referenceId": "variant-001",
"title": "Black / Large",
"imageUrl": "https://cdn.example.com/images/black-tshirt-lg.png"
},
"imageUrl": "https://cdn.example.com/images/black-tshirt.png",
"type": "extended_warranty",
"lineItemTransactionId": "6c7278d7-37a5-4253-ba8d-3d266bc68b2a",
"contractValue": 4999
}
],
"incident": {
"FailureType": "shipment_lost",
"FailureDescription": "The package has been rerouted to the correct destination.",
"Shipment": "1Z57E4700306164050",
"IncidentDate": 1756699200000,
},
"customerSegment": "premium_member",
"riskScore": 0,
"claimPhotos": {
"required": 1,
"uploaded": 1
},
"reportedAt": 1740766088101,
"sendDate": 1740766088101,
"type": "claim_approved",
"url": "https://servicer-webhook-url/post-webhook",
"claimDetailsUrl": "https://merchants.extend.com/store/claims/fc47a847-27c8-48c3-a564-68300f5f625b",
"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": 2000,
"title": "black t-shirt",
"serialNumber": "SN-2024-XK7892",
"selectedVariant": {
"referenceId": "variant-001",
"title": "Black / Large",
"imageUrl": "https://cdn.example.com/images/black-tshirt-lg.png"
},
"imageUrl": "https://cdn.example.com/images/black-tshirt.png",
"type": "extended_warranty",
"lineItemTransactionId": "6c7278d7-37a5-4253-ba8d-3d266bc68b2a",
"contractValue": 4999
}
],
"incident": {
"FailureType": "failure_electrical",
"FailureDescription": "a",
"FailureCause": "weather",
"IncidentDate": 1756699200000
},
"reportedAt": 1740766088101,
"sendDate": 1740766088101,
"type": "claim_denied",
"url": "https://servicer-webhook-url/post-webhook",
"claimDetailsUrl": "https://merchants.extend.com/store/claims/fc47a847-27c8-48c3-a564-68300f5f625b",
"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.
200Webhook received and processed successfully
400Invalid webhook payload
401Webhook signature verification failed