Rivya AI Docs

API Webhooks

Create signed Rivya API webhook endpoints, verify delivery signatures, inspect delivery attempts, and send safe test events.

Last reviewed on 2026/05/11

Use API webhooks when your integration needs Rivya to notify your server after a Public API generation reaches a terminal state.

Polling GET /api/v1/generations/{taskId} is still supported. Webhooks add signed callbacks for production systems that prefer event delivery.

Required Scope

Webhook management requires an API key with:

webhooks:manage

New keys created in Settings include this scope by default.

Create Endpoint

POST /api/v1/webhooks
curl https://rivya.ai/api/v1/webhooks \
  -H "Authorization: Bearer rvya_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production webhook",
    "url": "https://example.com/rivya/webhook",
    "event_types": ["generation.succeeded", "generation.failed"]
  }'

The response includes signing_secret only once:

{
  "id": "whend_...",
  "object": "webhook_endpoint",
  "name": "Production webhook",
  "url": "https://example.com/rivya/webhook",
  "event_types": ["generation.succeeded", "generation.failed"],
  "status": "active",
  "secret_preview": "whsec_12...abc123",
  "signing_secret": "whsec_...",
  "last_success_at": null,
  "last_failure_at": null,
  "failure_count": 0,
  "created_at": "2026-05-11T00:00:00.000Z",
  "updated_at": "2026-05-11T00:00:00.000Z",
  "disabled_at": null,
  "revoked_at": null
}

Store the full secret on your server. If you lose it, call the rotate endpoint and update your receiver.

URL Rules

Endpoint URLs must be HTTPS. Rivya rejects URLs with credentials, fragments, localhost names, local network addresses, private IP ranges, loopback addresses, and reserved addresses.

Rivya always sends:

  • POST
  • Content-Type: application/json
  • no custom user-controlled request headers
  • no automatic redirect following
  • a short delivery timeout

Events

Current event types:

  • generation.succeeded
  • generation.failed

Webhook payloads use the same public generation serializer as the status endpoint.

{
  "id": "evt_...",
  "type": "generation.succeeded",
  "api_version": "2026-05-11",
  "created_at": "2026-05-11T00:00:00.000Z",
  "data": {
    "generation": {
      "id": "task_public_id",
      "status": "succeeded",
      "model": "z-image",
      "reserved_credits": 1,
      "final_credits": 1,
      "created_at": "2026-05-11T00:00:00.000Z",
      "updated_at": "2026-05-11T00:01:00.000Z",
      "result": {
        "primary_url": "https://...",
        "urls": ["https://..."]
      },
      "error": null
    }
  }
}

Delivery Headers

Each delivery includes:

Rivya-Webhook-Id: evt_...
Rivya-Webhook-Timestamp: 1778467200
Rivya-Webhook-Signature: v1=<hex-hmac-sha256>
Rivya-Webhook-Attempt: 1
Rivya-Webhook-Endpoint-Id: whend_...
Rivya-Request-Id: req_...

Signature input:

${timestamp}.${rawBody}

Algorithm:

HMAC-SHA256 with the endpoint signing secret

Reject requests with stale timestamps. A five-minute tolerance is a practical default.

JavaScript Verification

import crypto from "node:crypto";

function verifyRivyaWebhook({ rawBody, headers, signingSecret }) {
  const timestamp = headers["rivya-webhook-timestamp"];
  const signature = headers["rivya-webhook-signature"] || "";
  const actual = signature.split(",").find((part) => part.startsWith("v1="))?.slice(3);
  const expected = crypto
    .createHmac("sha256", signingSecret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  if (!actual) return false;
  return crypto.timingSafeEqual(Buffer.from(actual, "hex"), Buffer.from(expected, "hex"));
}

Python Verification

import hmac
import hashlib

def verify_rivya_webhook(raw_body: str, headers: dict, signing_secret: str) -> bool:
    timestamp = headers.get("rivya-webhook-timestamp", "")
    signature = headers.get("rivya-webhook-signature", "")
    actual = next((part[3:] for part in signature.split(",") if part.startswith("v1=")), "")
    expected = hmac.new(
        signing_secret.encode(),
        f"{timestamp}.{raw_body}".encode(),
        hashlib.sha256,
    ).hexdigest()
    return bool(actual) and hmac.compare_digest(actual, expected)

Manage Endpoints

GET /api/v1/webhooks
GET /api/v1/webhooks/{endpointId}
PATCH /api/v1/webhooks/{endpointId}
DELETE /api/v1/webhooks/{endpointId}
POST /api/v1/webhooks/{endpointId}/rotate-secret
curl https://rivya.ai/api/v1/webhooks \
  -H "Authorization: Bearer rvya_sk_..."

curl https://rivya.ai/api/v1/webhooks/whend_... \
  -H "Authorization: Bearer rvya_sk_..."

curl -X PATCH https://rivya.ai/api/v1/webhooks/whend_... \
  -H "Authorization: Bearer rvya_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"status":"disabled"}'

curl -X POST https://rivya.ai/api/v1/webhooks/whend_.../rotate-secret \
  -H "Authorization: Bearer rvya_sk_..."

DELETE /api/v1/webhooks/{endpointId} disables the endpoint. It does not delete delivery history.

Delivery Records

List recent events:

GET /api/v1/webhook-events
curl https://rivya.ai/api/v1/webhook-events \
  -H "Authorization: Bearer rvya_sk_..."

List delivery attempts for an endpoint:

GET /api/v1/webhooks/{endpointId}/deliveries
curl https://rivya.ai/api/v1/webhooks/whend_.../deliveries \
  -H "Authorization: Bearer rvya_sk_..."

Delivery records include status, HTTP status, attempt number, request id, duration, truncated response snippet, and public error fields.

Test Event

Send a safe test payload:

POST /api/v1/webhooks/{endpointId}/test
curl -X POST https://rivya.ai/api/v1/webhooks/whend_.../test \
  -H "Authorization: Bearer rvya_sk_..."

The test event uses webhook.test. It does not create a generation task, does not consume credits, and does not include a real result URL.

Retry Policy

Rivya treats HTTP 2xx as success.

Failures include network errors, timeout, redirect responses, and non-2xx responses. Rivya retries up to five attempts:

  • immediately
  • after 1 minute
  • after 5 minutes
  • after 30 minutes
  • after 2 hours

After the final attempt, the event is marked failed.

Webhook delivery failures do not change generation status, credits, refunds, or task history.

Security Checklist

  • Verify the HMAC signature before parsing business logic.
  • Reject stale timestamps.
  • Treat test events separately from generation events.
  • Do not log full signing secrets.
  • Return 2xx only after your receiver has accepted the event.
  • Keep polling as a fallback for reconciliation.

Table of Contents