Webhook API Reference
Complete REST API reference for managing event relay webhook subscriptions to VECU platform events.
Base URL: https://{cip_vecu_config_url}
Authentication
All endpoints require an OAuth2 Bearer token. See Authentication for how to obtain a token.
Include the token in all requests:
Authorization: Bearer YOUR_ACCESS_TOKEN
POST /event-relay/subscriptions
Register a new webhook subscription.
Request Body
urlstringrequiredHTTPS endpoint URL. Must be publicly accessible and pass SSRF validation (no private IPs, no localhost).
authConfigobjectrequiredAuthentication configuration. Uses a discriminated union on the type
field. See Auth Configurations below.
eventFiltersobjectEvent filtering configuration. Defaults to catch-all (deliver everything) if omitted. See Event Filters.
Auth Configurations
HMAC_SHA256 (recommended):
The HMAC secret is auto-generated server-side (256-bit, hex-encoded). You do not provide a secret — the platform generates one and returns it once in the creation response. Store it immediately.
{
"url": "https://your-app.com/webhooks/vecu",
"authConfig": {
"type": "HMAC_SHA256"
}
}
OAUTH2:
{
"url": "https://your-app.com/webhooks/vecu",
"authConfig": {
"type": "OAUTH2",
"tokenUrl": "https://auth.your-app.com/oauth/token",
"clientId": "vecu-relay-client",
"clientSecret": "client-secret-abc123",
"scopes": ["webhook.receive"],
"grantType": "client_credentials"
}
}
tokenUrlstringrequiredOAuth2 token endpoint. Must use HTTPS.
clientIdstringrequiredOAuth2 client ID.
clientSecretstringrequiredOAuth2 client secret.
scopesstring[]OAuth2 scopes to request. Defaults to empty.
grantTypestringDefault: client_credentialsOAuth2 grant type.
BEARER:
{
"authConfig": {
"type": "BEARER",
"token": "your-static-bearer-token"
}
}
BASIC:
{
"authConfig": {
"type": "BASIC",
"username": "webhook_user",
"password": "secure_password_123"
}
}
NONE:
Using type: "NONE" means the relay will not authenticate when delivering
events to your endpoint. Your endpoint will have no way to verify that
requests originate from the VECU platform. This is strongly discouraged for
production use -- prefer HMAC_SHA256 or OAUTH2.
{
"authConfig": {
"type": "NONE"
}
}
Response
201 Created
{
"subscriptionId": "aBcDeFgHiJkLmNoPqRsT",
"url": "https://your-app.com/webhooks/vecu",
"authType": "HMAC_SHA256",
"authConfig": {
"type": "HMAC_SHA256",
"secret": "a1b2c3d4e5f6...64-hex-chars"
},
"eventFilters": {
"include": [],
"exclude": [],
"patterns": [],
"productGroups": []
},
"status": "active",
"createdAt": "2026-03-16T14:30:00Z",
"updatedAt": "2026-03-16T14:30:00Z"
}
For HMAC_SHA256 subscriptions, the authConfig.secret is only returned in
plaintext on creation. All subsequent API responses redact the secret. Store
it immediately.
GET /event-relay/subscriptions
List all webhook subscriptions for the authenticated client.
Query Parameters
nextTokenstringCursor token for pagination. Only present in the response when more results are available. Returns up to 25 subscriptions per page.
Response
200 OK
{
"items": [
{
"subscriptionId": "aBcDeFgHiJkLmNoPqRsT",
"url": "https://your-app.com/webhooks/vecu",
"authType": "HMAC_SHA256",
"authConfig": {
"type": "HMAC_SHA256"
},
"eventFilters": {
"include": ["custody.*"],
"exclude": [],
"patterns": [],
"productGroups": []
},
"status": "active",
"createdAt": "2026-03-16T14:30:00Z",
"updatedAt": "2026-03-16T14:30:00Z"
}
],
"nextToken": "eyJQSyI6..."
}
GET /event-relay/subscriptions/{subscriptionId}
Retrieve details of a specific subscription.
Response
200 OK
{
"subscriptionId": "aBcDeFgHiJkLmNoPqRsT",
"url": "https://your-app.com/webhooks/vecu",
"authType": "HMAC_SHA256",
"authConfig": {
"type": "HMAC_SHA256"
},
"eventFilters": {
"include": ["custody.*"],
"exclude": [],
"patterns": [],
"productGroups": []
},
"status": "active",
"createdAt": "2026-03-16T14:30:00Z",
"updatedAt": "2026-03-16T14:30:00Z"
}
404 Not Found
{
"code": "404",
"message": "Subscription not found"
}
PATCH /event-relay/subscriptions/{subscriptionId}
Update an existing subscription. Only include fields you want to change.
Updatable Fields
urlstringWebhook endpoint URL (re-validated for SSRF and duplicate check).
authConfigobjectAuthentication configuration. Credentials are re-encrypted on update. For HMAC_SHA256, a new secret is generated and returned once.
eventFiltersobjectEvent subscription filters.
statusstringactive or paused.
Example
curl -X PATCH https://{cip_vecu_config_url}/event-relay/subscriptions/aBcDeFgHiJkLmNoPqRsT \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": "paused"}'
Response
200 OK -- Returns the full updated subscription object.
DELETE /event-relay/subscriptions/{subscriptionId}
Delete a subscription. Credentials are deleted with the subscription record.
Response
204 No Content -- Empty body.
404 Not Found -- Subscription does not exist.
Event Filters
Control which events are delivered to your subscription.
Filter Options
{
"eventFilters": {
"include": ["custody.vehicle.released", "credential.revoked"],
"exclude": ["credential.expired"],
"patterns": ["custody.*"],
"productGroups": ["CUSTODY_SDK"]
}
}
includestring[]Exact event type names to receive.
excludestring[]Event types to skip (evaluated first, highest priority).
patternsstring[]Wildcard patterns using * (e.g., custody.*, verification.*).
productGroupsstring[]Product group names: CUSTODY_SDK or IDV_SDK. Expands to all namespaces
in the group.
Evaluation order (short-circuit):
- exclude -- if matched, skip
- include -- exact match delivers
- patterns -- wildcard match delivers
- productGroups -- expanded namespace match delivers
- All lists empty -- catch-all, delivers everything
- No match -- skip
Constraints:
- Maximum 50 items per list
- Wildcard only at the end of a segment (
custody.*is valid,*.credential.*is not) - Empty filter config = catch-all (delivers everything)
Error Responses
| HTTP Status | Description |
|---|---|
| 400 | Invalid request body or parameters |
| 401 | Missing or invalid access token |
| 403 | Access denied (subscription belongs to another client) |
| 404 | Subscription not found |
| 409 | URL already registered for this client |
| 500 | Internal server error |
Delivery Behavior
Retry Policy
When your webhook endpoint is unavailable or returns an error, the relay retries delivery automatically:
- 6 attempts total with exponential backoff
- Retried: HTTP 5xx responses, HTTP 429 responses, connection timeouts
- Not retried: HTTP 4xx responses (except 429) — these indicate a client-side rejection
- Events that exhaust all retries are moved to a dead letter queue (DLQ)
The X-VECU-Retry-Count request header on each delivery indicates how many previous attempts were made (0 for first attempt).
At-Least-Once Delivery
The relay provides at-least-once delivery semantics. In rare cases (network retries, regional failover), your endpoint may receive the same event more than once. Use the CloudEvents id field (also available as X-VECU-Event-ID header) for deduplication.
Delivery Timeout
Your endpoint must respond within 30 seconds. Requests that do not receive a response within this window are treated as failures and trigger a retry.