Skip to content

Continuous Monitoring & Webhook Signatures

Authenticated project sessions can establish continuous hourly resolution watches:

POST /v1/session/projects/prj_01J9X/watches HTTP/1.1
Host: did.is
Content-Type: application/json
{
"did": "did:web:identity.foundation"
}

Register a private HTTPS receiver to receive signed change alerts:

POST /v1/session/projects/prj_01J9X/webhook HTTP/1.1
Host: did.is
Content-Type: application/json
{
"url": "https://api.yourcompany.com/webhooks/didis"
}

Response:

{
"config": {
"id": "whc_01J9X...",
"url": "https://api.yourcompany.com/webhooks/didis",
"active": true
},
"secret": "whsec_32_byte_random_secret..."
}

Save secret immediately. It is displayed exactly once and cannot be recovered.


  • Header: X-DIDIS-Signature: t=<creationTimestamp>,v1=<64_char_hex>
  • Header: X-DIDIS-Event-ID: cevent_<64_char_hex>
  • Hash algorithm: HMAC-SHA256
  • Signed payload: <creationTimestamp>.<rawBodyBytes>
import { verifyCustomerMonitorSignature } from "@didis/client";
// In your Express, Next.js, or Fastify webhook handler:
export async function handleWebhook(req: Request) {
const rawBody = await req.text();
const signature = req.headers.get("x-didis-signature") ?? "";
const eventId = req.headers.get("x-didis-event-id") ?? "";
const isValid = await verifyCustomerMonitorSignature(
process.env.DIDIS_WEBHOOK_SECRET!,
rawBody,
signature,
{
tenantId: "tenant_01J9X...",
projectId: "project_01J9X...",
eventId: eventId
},
300 // Max age in seconds (5 minutes)
);
if (!isValid) {
return new Response("Invalid signature", { status: 401 });
}
const event = JSON.parse(rawBody);
console.log(`Received event: ${event.type} for DID: ${event.did}`);
return new Response("OK", { status: 200 });
}
import os
from didis import verify_customer_monitor_signature
def handle_webhook(request_body_bytes: bytes, headers: dict) -> bool:
signature = headers.get("X-DIDIS-Signature", "")
event_id = headers.get("X-DIDIS-Event-ID", "")
return verify_customer_monitor_signature(
secret=os.environ["DIDIS_WEBHOOK_SECRET"],
body=request_body_bytes,
signature=signature,
tenant_id="tenant_01J9X...",
project_id="project_01J9X...",
event_id=event_id,
max_age_seconds=300
)

Replay Tolerance & Deduplication Best Practices

Section titled “Replay Tolerance & Deduplication Best Practices”
  1. Deduplicate event.id: Delivery is at least once. Store processed event.id values in Redis or your database with a 30-day TTL. If an event ID has already been processed, return 200 OK immediately without re-executing side effects.
  2. Creation Timestamp Immutability: The signed timestamp t is fixed at event creation time and remains unchanged across retry attempts.
  3. Delivery Retry Schedule: Failed deliveries retry up to 5 times (backoff: 60s, 120s, 240s, 480s) before transitioning to DEAD_LETTER.