Configure the PV service webhook

How to configure the PV service webhook that receives the successful verification event from KWS.

4 mins to read

Introduction

When a parent successfully verifies their identity using the PV service, KWS notifies your system by sending the parent-verified event via a webhook call.

To configure the webhook on your side, you need to:

NOTE: Your Test environment and Production environment each come with their own unique webhook secret code.

Set up your webhook URL

  1. In the Parent Verification tab, enter the Successful verification webhook URL. This is where KWS sends the webhook that confirms successful verification.

IMPORTANT:

  • Do not include information in the webhook URL that is sensitive to manipulation by an attacker (for example, numeric IDs that may be iterated over easily).
  • Do not include special characters in your URL.
  1. Click Publish to Test.

Generate your signature using the secret code

After you click Publish to Test, the Parent Verification tab displays the Webhook secret code:

Secret code

Click image for full size.

Example

This example demonstrates how to use the secret code to create the signature.

KWS sends you a webhook. The webhook call is a HTTPS POST with a common structure wrapping the event-specific payload:

POST https://www.yourdomain.com/your-webhook-endpoint
x-kws-signature: t=1621535329,v1=f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
Body
{
"name": "webhook-name",
"time": "ISO8601 timestamp",
"orgId": "uuid",
"productId": "uuid" | null,
"environmentId": "uuid" | null,
"payload": { ... type-specific payload ... }
}

The header of the POST call has this structure:

x-kws-signature: t=<timestamp>,v1=<signature>[,v1=<signature>]

where <signature> is generated via HMAC_SHA256 using the timestamp value in the header, the Webhook secret code displayed in your product's integration details in the Parent Verification tab, and the POST body.

const timestamp = /* extracted value of 't=' from the header */;
const body = /* raw request body as UTF-8 string */;
const secretKey = /* secret key, as provided in the Developer Portal */;
const signature = crypto
.createHmac('sha256', secretKey)
.update('${timestamp}.${body}')
.digest('hex');

On your product side, you need to create the same signature using the above code and the timestamp, the Webhook secret code, and the POST body. If the signature you created matches the v1 signature we passed to you in the header of our webhook call, you can confirm that the webhook was sent by us.

Surface a public endpoint via your webserver/web application (similar to how you build any other API endpoint). This endpoint takes the POST body described below, and validates the signature header.

NOTE: It is important that the raw POST body is used. Do not attempt to parse and re-stringify the JSON as this may result in mismatched signatures due to inconsistencies in JSON library implementations.

POST body structure

POST https://www.yourdomain.com/your-webhook-endpoint
x-kws-signature: t=<timestamp>,v1=<signature>[,v1=<signature>]
Body
{
"name": "webhook-name",
"time": "ISO8601 timestamp",
"orgId": "uuid",
"productId": "uuid" | null,
"environmentId": "uuid" | null,
"payload": { ... type-specific payload ... }
}

Signatures

To confirm that a webhook is truly coming from KWS, compare the v1 signature you generate with the v1 signature in the header of the webhook call you receive from KWS. If the signatures match, you can confirm that the call came from the KWS.

Signatures are a HMAC_SHA256 hash in hex format (lowercase), where the secret key is the Webhook secret code we provide for you in the Parent Verification tab.

Signatures are sent in a x-kws-signature header in the following format:

x-kws-signature: t=<timestamp>,v1=<signature>[,v1=<signature>]

NOTE: Currently only a single v1 signature will be included, but the header may contain multiple signatures if/when:

  • Signature key rotation is supported (signatures will be sent for previous and current keys for a transitionary period).
  • The signature algorithm changes (v2 signatures will be sent alongside v1 signatures for a transitionary period).

The algorithm to generate v1 signatures is a HMAC_SHA256 of the timestamp included in the signature header, followed by a period (‘.') followed by the raw request body. Initialize the HMAC with your webhook's secret code.

For example:

const timestamp = /* extracted value of ‘t=' from the header */;
const body = /* raw request body as UTF-8 string */;
const secretKey = /* the webhook secret code from the %Parent Verification% tab. */;
const signature = crypto
.createHmac('sha256', secretKey)
.update(`${timestamp}.${body}`)
.digest('hex');

NOTE: The webhook URL is intentionally not part of the signature algorithm to allow easier signature validation within services behind reverse-proxies/load-balancers etc. when the request URL may be modified.

Webhook retry behavior

The webhook retry behavior is dependent on the status code your server returns to the POST request:

Status CodesBehavior
200 to 299Call assumed to be successful.
0-199, 500+, timeout (3 seconds) or network errorTransient failure.The call will be retried with exponential backoff, after a delay of:
  • 30 seconds
  • 1 minute (1.5 min total)
  • 2 minutes (3.5 min total)
  • 4 minutes (7.5 min total)
  • 8 minutes (15.5 min total)
  • 16 minutes (31.5 min total)
  • 32 minutes (1 hour 3.5 min total)
  • 1 hour 4 minutes (2 hours 7.5 mins total)
  • 2 hours 8 minutes (4 hours 15.5 mins total)
  • 4 hours 16 minutes (8 hours 31.5 mins total)
  • 8 hours 32 minutes (17 hours 3.5 mins total)
  • 17 hours 4 minutes (34 hours 7.5 mins total)

If the final retry fails (status code 400+), the call is logged as failed.

Next step

Choose which verification methods will be available to parents (subject to region) in the verification webview.