MinervaMinerva
Documentation
API Reference

Webhooks

Receive real-time HTTP callbacks when proofs complete, fail, or expire.

Webhooks let your application receive real-time notifications when proof-related events occur. Rather than polling the API, Minerva sends an HTTP POST to your endpoint whenever a proof completes, fails, or expires.

Endpoints

MethodPathDescription
POST/api/v1/webhooksRegister a new webhook endpoint
GET/api/v1/webhooksList all registered webhooks for your account
GET/api/v1/webhooks/:idGet a specific webhook by ID
PATCH/api/v1/webhooks/:idUpdate a webhook (URL, events, active state)
DELETE/api/v1/webhooks/:idRemove a webhook permanently

Register a Webhook

http
POST /api/v1/webhooks HTTP/1.1
Host: api.zkesg.com
Authorization: Bearer <your-api-key>
Content-Type: application/json

{
  "url": "https://your-app.com/minerva/webhook",
  "events": ["proof.completed", "proof.failed"],
  "description": "Production proof completion handler"
}
FieldTypeRequiredDescription
urlstringYesHTTPS endpoint to receive events (must be publicly accessible)
eventsstring[]YesArray of event types to subscribe to (see Event Types below)
descriptionstringNoHuman-readable notes about the webhook
activebooleanNoWhether the webhook is active (default: true)
json
{
  "id": "wh_01HX5MQZR9BNT5E2FPMCDVWK4",
  "url": "https://your-app.com/minerva/webhook",
  "events": ["proof.completed", "proof.failed"],
  "secret": "whsec_a7f3c2e9d1b84f6a2e0c5d8b3f7a1e4c",
  "active": true,
  "created_at": "2026-03-16T12:00:00Z"
}
Warning: The secret field is shown only once. Store it securely — you will use it to verify webhook signatures.

Event Types

EventWhen it fires
proof.completedProof generation succeeded and the proof is ready
proof.failedProof generation failed (constraint error, invalid inputs, timeout)
proof.expiredA proof older than the retention window was removed
proof.verifiedA proof was verified via GET /api/v1/proofs/:id/verify
key.createdA new API key was created on your account
key.revokedAn API key was revoked

Payload Format

json
{
  "id": "evt_01HX6NRZR9BNT5E2FPMCDVWM5",
  "type": "proof.completed",
  "created_at": "2026-03-16T14:23:11Z",
  "data": {
    "proof_id": "proof_01HX4KQZR9BNT5E2FPMCDVWJ3",
    "circuit": "carbon-emissions",
    "status": "completed",
    "proof_blob": "WINTERFELL_PROOF_AAAAE...",
    "public_inputs": { "threshold": 2500 },
    "duration_ms": 1842,
    "created_at": "2026-03-16T14:23:09Z"
  }
}

HMAC Signature Verification

Every webhook request includes a Minerva-Signature header. Verify it to ensure the request came from Minerva and was not tampered with.

bash
# Header format
Minerva-Signature: t=1742133660,v1=a7f3c2e9d1b84f6a2e0c5d8b3f7a1e4c9d2b5f8a3e6c1...

# To verify:
# 1. Extract timestamp (t) and signature (v1)
# 2. Construct the signed payload: "<timestamp>.<raw_body>"
# 3. Compute HMAC-SHA256 of signed payload using your webhook secret
# 4. Compare with the v1 value (constant-time comparison)
javascript
import crypto from 'crypto';

export function verifyMinervaSignature(
  rawBody: string,
  signatureHeader: string,
  webhookSecret: string
): boolean {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map(p => p.split('='))
  );
  const timestamp = parts['t'];
  const expected = parts['v1'];

  if (!timestamp || !expected) return false;

  // Reject events older than 5 minutes
  if (Date.now() / 1000 - Number(timestamp) > 300) return false;

  const payload = `${timestamp}.${rawBody}`;
  const computed = crypto
    .createHmac('sha256', webhookSecret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(computed),
    Buffer.from(expected)
  );
}

Retry Behaviour

  • Minerva retries failed deliveries (non-2xx or timeout) up to 5 times
  • Retry schedule: 30s, 5m, 30m, 2h, 8h after the initial failure
  • After 5 failures, the webhook is automatically deactivated
  • Respond with 2xx as quickly as possible — process events asynchronously
  • Idempotency: use the event id (evt_...) to deduplicate retries

Update a Webhook

http
PATCH /api/v1/webhooks/wh_01HX5MQZR9BNT5E2FPMCDVWK4 HTTP/1.1
Host: api.zkesg.com
Authorization: Bearer <your-api-key>
Content-Type: application/json

{
  "events": ["proof.completed", "proof.failed", "proof.verified"],
  "active": true
}

Delete a Webhook

bash
curl -X DELETE "https://api.zkesg.com/api/v1/webhooks/wh_01HX5MQZR9BNT5E2FPMCDVWK4" \
  -H "Authorization: Bearer $MINERVA_API_KEY"
Documentation
API Reference

Webhooks

Receive real-time HTTP callbacks when proofs complete, fail, or expire.

Webhooks let your application receive real-time notifications when proof-related events occur. Rather than polling the API, Minerva sends an HTTP POST to your endpoint whenever a proof completes, fails, or expires.

Endpoints

MethodPathDescription
POST/api/v1/webhooksRegister a new webhook endpoint
GET/api/v1/webhooksList all registered webhooks for your account
GET/api/v1/webhooks/:idGet a specific webhook by ID
PATCH/api/v1/webhooks/:idUpdate a webhook (URL, events, active state)
DELETE/api/v1/webhooks/:idRemove a webhook permanently

Register a Webhook

http
POST /api/v1/webhooks HTTP/1.1
Host: api.zkesg.com
Authorization: Bearer <your-api-key>
Content-Type: application/json

{
  "url": "https://your-app.com/minerva/webhook",
  "events": ["proof.completed", "proof.failed"],
  "description": "Production proof completion handler"
}
FieldTypeRequiredDescription
urlstringYesHTTPS endpoint to receive events (must be publicly accessible)
eventsstring[]YesArray of event types to subscribe to (see Event Types below)
descriptionstringNoHuman-readable notes about the webhook
activebooleanNoWhether the webhook is active (default: true)
json
{
  "id": "wh_01HX5MQZR9BNT5E2FPMCDVWK4",
  "url": "https://your-app.com/minerva/webhook",
  "events": ["proof.completed", "proof.failed"],
  "secret": "whsec_a7f3c2e9d1b84f6a2e0c5d8b3f7a1e4c",
  "active": true,
  "created_at": "2026-03-16T12:00:00Z"
}
Warning: The secret field is shown only once. Store it securely — you will use it to verify webhook signatures.

Event Types

EventWhen it fires
proof.completedProof generation succeeded and the proof is ready
proof.failedProof generation failed (constraint error, invalid inputs, timeout)
proof.expiredA proof older than the retention window was removed
proof.verifiedA proof was verified via GET /api/v1/proofs/:id/verify
key.createdA new API key was created on your account
key.revokedAn API key was revoked

Payload Format

json
{
  "id": "evt_01HX6NRZR9BNT5E2FPMCDVWM5",
  "type": "proof.completed",
  "created_at": "2026-03-16T14:23:11Z",
  "data": {
    "proof_id": "proof_01HX4KQZR9BNT5E2FPMCDVWJ3",
    "circuit": "carbon-emissions",
    "status": "completed",
    "proof_blob": "WINTERFELL_PROOF_AAAAE...",
    "public_inputs": { "threshold": 2500 },
    "duration_ms": 1842,
    "created_at": "2026-03-16T14:23:09Z"
  }
}

HMAC Signature Verification

Every webhook request includes a Minerva-Signature header. Verify it to ensure the request came from Minerva and was not tampered with.

bash
# Header format
Minerva-Signature: t=1742133660,v1=a7f3c2e9d1b84f6a2e0c5d8b3f7a1e4c9d2b5f8a3e6c1...

# To verify:
# 1. Extract timestamp (t) and signature (v1)
# 2. Construct the signed payload: "<timestamp>.<raw_body>"
# 3. Compute HMAC-SHA256 of signed payload using your webhook secret
# 4. Compare with the v1 value (constant-time comparison)
javascript
import crypto from 'crypto';

export function verifyMinervaSignature(
  rawBody: string,
  signatureHeader: string,
  webhookSecret: string
): boolean {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map(p => p.split('='))
  );
  const timestamp = parts['t'];
  const expected = parts['v1'];

  if (!timestamp || !expected) return false;

  // Reject events older than 5 minutes
  if (Date.now() / 1000 - Number(timestamp) > 300) return false;

  const payload = `${timestamp}.${rawBody}`;
  const computed = crypto
    .createHmac('sha256', webhookSecret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(computed),
    Buffer.from(expected)
  );
}

Retry Behaviour

  • Minerva retries failed deliveries (non-2xx or timeout) up to 5 times
  • Retry schedule: 30s, 5m, 30m, 2h, 8h after the initial failure
  • After 5 failures, the webhook is automatically deactivated
  • Respond with 2xx as quickly as possible — process events asynchronously
  • Idempotency: use the event id (evt_...) to deduplicate retries

Update a Webhook

http
PATCH /api/v1/webhooks/wh_01HX5MQZR9BNT5E2FPMCDVWK4 HTTP/1.1
Host: api.zkesg.com
Authorization: Bearer <your-api-key>
Content-Type: application/json

{
  "events": ["proof.completed", "proof.failed", "proof.verified"],
  "active": true
}

Delete a Webhook

bash
curl -X DELETE "https://api.zkesg.com/api/v1/webhooks/wh_01HX5MQZR9BNT5E2FPMCDVWK4" \
  -H "Authorization: Bearer $MINERVA_API_KEY"