Skip to main content

Generic Webhook — API Reference

Developers who need exact request/response specs for the Configuration API.


Configuration API

Internal use only

Do not share these URLs or credentials with clients. These URLs are of the lambdas and not the API gateway URLs.

EnvironmentURL
Productionhttps://a4l5wq5l6gjkjrromxx3riovu40lpzcx.lambda-url.ap-south-1.on.aws/
Developmenthttps://zi2btyam4ewqn4pb6kfz4bbkoq0yvwpg.lambda-url.ap-south-1.on.aws/

All endpoints use the same base URL. Method (GET / POST / PATCH) determines the operation.

Configuration API flow


Authentication

Every request must pass two checks — IP whitelist first, then credentials.

1. IP Whitelist
  • Controlled by ALLOWED_IPS env var (comma-separated list, or * for all)
  • Checked before credentials — if IP is not allowed, the request is rejected immediately
  • If ALLOWED_IPS is not set at all, every request is rejected

Response (403) — IP not whitelisted:

{
"statusCode": 403,
"status": "failed",
"error": "IP not whitelisted"
}
2. Credentials (choose one method)

Method A — JWT (RS256):

Authorization: Bearer <jwt-token>
  • Token verified using PUBLIC_CERT env var (RSA public key)
  • appId extracted from decoded JWT payload
  • Returns "Token Expired" error if token is expired

Method B — appId/appKey:

appid: <app-id>
appkey: <app-key>
  • Validated against https://auth.hyperverge.co/credentials/authenticate
  • MD5 digest of appKey used for cache key comparison
  • Credentials cached for 15 minutes in memory
  • Note: If the auth service returns a non-401 error (e.g. 500, timeout), the request is allowed through — intentional, to prevent auth outages from blocking config operations

Response (401) — Missing or invalid credentials:

{
"statusCode": 401,
"status": "failed",
"error": "Missing/Invalid credentials"
}

POST / — Create webhook config

Creates a new webhook config for a appId + eventType combination.

curl example:

curl --request POST 'https://a4l5wq5l6gjkjrromxx3riovu40lpzcx.lambda-url.ap-south-1.on.aws/' \
--header 'appid: <APP ID>' \
--header 'appkey: <APP KEY>' \
--header 'Content-Type: application/json' \
--data '{
"appId": "55a3a6",
"eventType": "FINISH_TRANSACTION_WEBHOOK",
"isActive": true,
"webhookUrl": "https://client.example.com/webhook",
"product": "link-kyc",
"deleteAppId": false,
"headers": {
"Authorization": "Bearer client-token"
},
"updateSecret": true
}'

Request body:

FieldTypeRequiredDescription
appIdstringYesClient's app ID (becomes DynamoDB partition key)
eventTypestringYesEvent type (becomes DynamoDB sort key)
webhookUrlstringYesDefault URL to deliver webhooks to
productstringYesProduct registering this config (e.g. "audit-portal", "link-kyc")
isActivebooleanYesWhether delivery is enabled
headersobjectNoCustom HTTP headers to include in every webhook request
deleteAppIdbooleanNoIf true, strips appId from payload before delivery
updateSecretbooleanNoIf true, generates a new HMAC secret and stores it
workflowsobjectNoPer-workflow URL map: { "workflowId": ["url1", "url2"] }

Note on updateSecret: If the env var UPDATE_SECRET_DEFAULT_VALUE=yes is set, a secret is always generated even without passing updateSecret: true.

Response (201) — Success:

{
"statusCode": 201,
"status": "success",
"message": "Config record successfully stored in Dynamo DB",
"data": {}
}

If updateSecret: true (or UPDATE_SECRET_DEFAULT_VALUE=yes), data contains the generated secret:

{
"statusCode": 201,
"status": "success",
"message": "Config record successfully stored in Dynamo DB",
"data": {
"secret": "whsecure_a1b2c3d4e5f6..."
}
}

Secret format: whsecure_ + 40 hex characters (crypto.randomBytes(20).toString('hex')).

Response (422) — Validation failure:

{
"statusCode": 422,
"status": "failed",
"error": "\"isActive\" is required"
}

Response (401) — Missing or invalid credentials:

{
"statusCode": 401,
"status": "failed",
"error": "Missing/Invalid credentials"
}

Response (500) — Server error:

{
"statusCode": 500,
"status": "failed",
"message": "Config record couldn't be stored. Please try again later"
}

GET / — Retrieve webhook config

Fetches the stored config for an appId + eventType.

curl example:

curl --request GET 'https://a4l5wq5l6gjkjrromxx3riovu40lpzcx.lambda-url.ap-south-1.on.aws/' \
--header 'appid: <APP ID>' \
--header 'appkey: <APP KEY>' \
--header 'Content-Type: application/json' \
--data '{
"appId": "55a3a6",
"eventType": "FINISH_TRANSACTION_WEBHOOK"
}'

curl example (with secret):

curl --request GET 'https://a4l5wq5l6gjkjrromxx3riovu40lpzcx.lambda-url.ap-south-1.on.aws/' \
--header 'appid: <APP ID>' \
--header 'appkey: <APP KEY>' \
--header 'Content-Type: application/json' \
--data '{
"appId": "55a3a6",
"eventType": "FINISH_TRANSACTION_WEBHOOK",
"shareSecret": true
}'

Request body:

FieldTypeRequiredDescription
appIdstringYesClient's app ID
eventTypestringYesEvent type
shareSecretbooleanNo (default: false)Include HMAC secret in response

The secret field is always stripped from the response unless shareSecret: true is explicitly passed.

Response (200) — Success:

{
"statusCode": 200,
"status": "success",
"message": "Config records successfully fetched from Dynamo DB",
"data": {
"product": "link-kyc",
"isActive": true,
"webhookUrl": "https://client.example.com/webhook",
"eventType": "FINISH_TRANSACTION_WEBHOOK",
"appId": "55a3a6",
"headers": { "Authorization": "Bearer client-token" }
}
}

With shareSecret: true, data also includes "secret": "whsecure_...".

Response (422) — Validation failure (e.g. extra field not allowed):

{
"statusCode": 422,
"status": "failed",
"error": "\"product\" is not allowed"
}

Response (422) — Missing required field:

{
"statusCode": 422,
"status": "failed",
"error": "\"eventType\" is required"
}

Response (401) — Missing or invalid credentials:

{
"statusCode": 401,
"status": "failed",
"error": "Missing/Invalid credentials"
}

PATCH / — Update webhook config

Updates fields on an existing config. Only provided fields are changed.

curl example:

curl --request PATCH 'https://a4l5wq5l6gjkjrromxx3riovu40lpzcx.lambda-url.ap-south-1.on.aws/' \
--header 'appid: <APP ID>' \
--header 'appkey: <APP KEY>' \
--header 'Content-Type: application/json' \
--data '{
"appId": "55a3a6",
"eventType": "FINISH_TRANSACTION_WEBHOOK",
"webhookUrl": "https://new-endpoint.example.com/webhook",
"updateSecret": true
}'

Request body:

FieldTypeRequiredDescription
appIdstringYesLookup key (partition key) — cannot be changed
eventTypestringYesLookup key (sort key) — cannot be changed
webhookUrlstringNoNew default webhook URL
productstringNoProduct identifier
isActivebooleanNoEnable or disable delivery
headersobjectNoCustom HTTP headers
updateSecretbooleanNoIf true, generates and stores a new HMAC secret
workflowsobjectNoPer-workflow URL map

appId and eventType identify the record to update and are never modified.

Response (200) — Success:

{
"statusCode": 200,
"status": "success",
"message": "Config record successfully updated in Dynamo DB",
"data": {
"Attributes": {
"webhookUrl": "https://new-endpoint.example.com/webhook"
}
}
}

data.Attributes contains the updated fields (DynamoDB UPDATED_NEW response).

If updateSecret: true, the new secret is returned in data.Attributes.secret.

Response (422) — Validation failure:

{
"statusCode": 422,
"status": "failed",
"error": "\"someKey\" is not allowed"
}

Response (401) — Missing or invalid credentials:

{
"statusCode": 401,
"status": "failed",
"error": "Missing/Invalid credentials"
}

DynamoDB config schema

Table is shared between Configuration API (writes) and Delivery Service (reads).

  • Partition key: appId (String)
  • Sort key: eventType (String)
  • Table name: from DYNAMODB_TABLE_NAME env var (Config API) / dynamoTableName env var (Delivery Service)
FieldTypeDescription
appIdStringClient's app ID
eventTypeStringEvent type (e.g. FINISH_TRANSACTION_WEBHOOK)
webhookUrlStringDefault URL to deliver webhooks to
secretStringHMAC signing secret (whsecure_ + 40 hex chars)
headersObjectCustom HTTP headers to include with every webhook request
isActiveBooleanfalse = paused (no delivery, no retry)
workflowsObjectPer-workflow URL map: { "workflowId": ["url1", "url2"] }
deleteAppIdBooleantrue = strip appId from payload before delivery
productStringProduct that registered this config

Delivery Service — SQS input format

The Lambda receives SQS messages containing S3 event notifications:

{
"Records": [
{
"messageId": "msg-123",
"body": "{\"Records\":[{\"s3\":{\"bucket\":{\"name\":\"prod-generic-webhook-ind\"},\"object\":{\"key\":\"audit-portal/55a3a6/2024-12-04/evt-123/FINISH_TRANSACTION_WEBHOOK/webhook.json\"}}}]}",
"attributes": {
"SentTimestamp": "1704067200000"
}
}
]
}

The body is a JSON string containing the S3 bucket name and object key.

Delivery Service — Lambda response format
{
"batchItemFailures": [
{ "itemIdentifier": "msg-456" }
]
}

Only failed messageIds are listed. Empty array = all records succeeded. SQS retries only the listed messages (partial batch retry via ReportBatchItemFailures).

Delivery Service — webhook delivery headers
POST {webhookUrl}
Content-Type: application/json
{custom headers from DynamoDB config}
x-hv-signature: {HMAC-SHA256 hex digest}
Timeout: {AXIOS_TIMEOUT}ms (default: 30000)
Was this helpful?
Ask AI

Ask anything about the internal documentation

AI answers are based on internal documentation. Verify critical information.