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. - Every webhook also carries an
X-Idempotency-Keyheader: a UUID that identifies the claim event being delivered. See Retries and idempotency below.
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 has the same shape for all of the following claim events. closedMetaData and closureMethod are sent only on claim_closed:
approved: 'claim_approved',
denied: 'claim_denied',
review: 'claim_in_review',
closed: 'claim_closed'
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: {
amount: number
currencyCode?: string
formattedAmount?: string
}
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' | 'claim_closed'
url: string
claimDetailsUrl: string
kid: string
// Optional fields
denialReason?: string
/** Adjudication result reason, when the claim was adjudicated */
reason?: string
customerSegment?: string
riskScore?: number
claimPhotos?: {
required: number
uploaded: number
}
// Sent only on the claim_closed event
/** Why and when the claim closed */
closedMetaData?: {
resolution:
| 'no_service'
| 'service_change'
| 'defective_product_not_shipped'
| 'no_images_submitted'
| 'identity_not_verified'
| 'shipment_received'
| 'no_service_required'
| 'failure_not_covered'
| 'resolved_with_alternative_method'
| 'address_not_verified'
/** Epoch ms when the claim was closed */
closedAt: number
}
/** Whether the closure was automatic or driven by an Extend or merchant agent */
closureMethod?: 'automated' | 'system_auto_close' | 'extend_agent' | 'merchant_agent'
}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",
"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": { "amount": 2000, "currencyCode": "USD" },
"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"
}Retries and idempotency
Respond with a 2xx status code. A webhook that does not receive a 2xx is retried every hour for up to 48 hours.
Extend may therefore deliver the same event more than once, for example after your endpoint returned a 5xx or timed out. Every delivery of an event carries the same X-Idempotency-Key. Store the keys you have processed and, when a webhook arrives with a key you have already handled, return a 2xx without re-processing it. Different events, including different status changes on the same claim, always carry different keys.
The payload reflects the claim's state at delivery time, so a redelivery may include newer claim details than the first attempt. A later change to the claim's status is sent as its own event with its own key.
200Webhook received and processed successfully
400Invalid webhook payload
401Webhook signature verification failed