Webhooks

Configure an HTTPS endpoint in Settings to receive balance.low, credits.added, key.created / rotated / revoked, key.spend_cap.warning / reached, key.expiring, account.daily_limit.reached, batch.completed / failed, video.completed / failed and model.deprecated. Each POST is JSON { id, type, created, data } signed with HMAC-SHA256.

A failed delivery (non-2xx or timeout) is retried after 30 seconds, 5 minutes and 30 minutes with the same event id — dedupe on it. Retries survive our restarts. Every attempt is listed in Settings, where you can redeliver any event. After 20 consecutive failures the webhook is paused.

import crypto from 'node:crypto'

// UltraGPT-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
export function verifyWebhook(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(',').map(p => p.split('=')))
  const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex')
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < toleranceSec
  return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ''))
}