Upvera for Developers
Value API

Webhooks

Upvera can call your server when your startup's Upvera Value moves by at least your threshold. Webhooks are part of the Scale plan. Add up to three HTTPS endpoints in Upvera under Connections › Value API.

Events

EventWhen
value.thresholdThe value moved by at least your threshold (0.1% to 50%) since the last notification, in the direction you chose: up, down or both. Checked every 5 minutes.
pingYou added an endpoint or pressed Send test.

The anchor moves on every threshold crossing in either direction, so you get one event per move, not a stream.

Payload

json
{
  "v": "upvera/webhook/v1",
  "id": "evt_…",
  "type": "value.threshold",
  "created_at": "2026-10-19T00:05:02Z",
  "data": {
    "startup": { "slug": "upvera-mthgaif7", "name": "Upvera", "url": "https://app.upvera.co/value/upvera-mthgaif7" },
    "value": 344.762011,
    "from": 341.213023,
    "change_pct": 1.04,
    "direction": "up",
    "threshold_pct": 1.0,
    "as_of": "2026-10-19T00:00:00Z",
    "from_at": "2026-10-18T21:00:00Z",
    "rule": "value@1"
  }
}

change_pct is computed by Upvera. Show it as it is.

Headers

text
Upvera-Signature: t=1760834702,v1=5f2b…
Upvera-Event: value.threshold
Upvera-Event-Id: evt_…
Upvera-Delivery-Attempt: 1
User-Agent: Upvera-Webhooks/1.0

Verify the signature

v1 is the hex HMAC-SHA256 of t + "." + raw body, keyed with your endpoint's secret (whsec_…). While you rotate a secret, the header carries two v1 values: accept the request if either matches. Reject timestamps older than 5 minutes. Verify before you parse the body.

js
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyUpvera(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.groupBy(header.split(','), (p) => p.split('=')[0]);
  const t = Number(parts.t?.[0]?.split('=')[1]);
  if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const want = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
  return (parts.v1 ?? []).some((p) => {
    const got = Buffer.from(p.slice(3), 'hex');
    return got.length === want.length && timingSafeEqual(got, want);
  });
}

Delivery

  • At least once. The same event can arrive twice: deduplicate on id, which is also the Upvera-Event-Id header.
  • Any 2xx acknowledges it. Anything else is retried with back-off from 1 minute to 6 hours, 12 attempts in about a day.
  • HTTPS only, redirects are not followed, the response body is never read, and a delivery times out after 8 seconds.
  • GET /api/v1/me/deliveries shows recent deliveries with their status, without bodies.

Updated 2026-10-19