Vunk
PUBLIC API · v1

Build on the industry’s connection graph

Key-gated, contact-free read access to the verified company, games and regulatory catalog — plus signed webhooks when the data changes. The same provenance-backed facts that power Vunk, available to your systems.

Base URL https://bevunk.com/v1/public

Authentication

Every request needs an API key. A company owner or admin issues keys for their company. The full secret (vunk_sk_…) is shown exactly once at creation — store it securely; we keep only a hash. Send it in the X-API-Key header, or as a Bearer token.

curl https://bevunk.com/v1/public/companies \
  -H "X-API-Key: $VUNK_API_KEY"

# or, equivalently
curl https://bevunk.com/v1/public/companies \
  -H "Authorization: Bearer $VUNK_API_KEY"

Scopes

Each key carries one or more scopes; a request to a surface outside the key’s scopes returns 403.

  • catalog:readCompanies and games — /v1/public/companies, /v1/public/games
  • regulatory:readJurisdictions and regulatory changes — /v1/public/jurisdictions

Rate limits are per key. Exceeding your budget returns 429 with a Retry-After header. Contacts are never included in any response.

Read endpoints

Cursor-paginated lists and slug detail. Lists accept limit and an opaque cursor; responses carry a next_cursor (null on the last page).

GET /v1/public/companies?limit=20&cursor=...   # scope: catalog:read
GET /v1/public/companies/:slug
GET /v1/public/games?limit=20                  # scope: catalog:read
GET /v1/public/games/:slug
GET /v1/public/jurisdictions?limit=20          # scope: regulatory:read
GET /v1/public/jurisdictions/:slug

# example response shape
{
  "items": [ { "slug": "...", "name": "...", "verified": true, "...": "..." } ],
  "next_cursor": "eyJrIjoi..."
}

Every fact carries provenance (source, confidence, last verified). Below the freshness threshold, data is excluded — by charter, stale data is never served.

Versioning, embeds & fair use

Every response carries an X-API-Version header and a Link: </developers>; rel="service-doc". When an endpoint is scheduled for retirement, its responses add the IETF deprecation headers so your client can detect it automatically — at least 90 days before it stops working:

Deprecation: true
Sunset: Wed, 01 Jan 2027 00:00:00 GMT
Link: </developers#deprecation>; rel="deprecation"

Embed a status badge

Show your verified status anywhere with a cacheable SVG badge — no key required:

<img src="https://bevunk.com/v1/public/embed/companies/YOUR-SLUG/badge.svg"
     alt="Status on Vunk" height="20">

Fair use & data canaries

The catalog is free to read but not to re-publish wholesale. Key-gated responses carry a per-key X-Vunk-Trace watermark; if exported data resurfaces elsewhere it identifies the source key. Don’t resell the raw dataset, and cache responsibly — there are no contacts in the API to scrape, by design.

Webhooks

Register an HTTPS endpoint to receive a signed POST when catalog or regulatory data changes — no polling. The signing secret is shown once at registration. Vunk retries with exponential backoff and auto-disables an endpoint after sustained failures.

Events

  • company.verifiedA company reached a new KYB verification level.
  • company.listing_status.changedA company’s earned standing changed.
  • game.releasedA game hit a launch or market-launch milestone.
  • jurisdiction.change.publishedA regulatory change was published for a jurisdiction.

Verifying the signature

Each delivery carries an X-Vunk-Signature header of the form t=<unix>,v1=<hex>, where v1 is the HMAC-SHA256 of "<t>.<raw-body>" keyed by your signing secret. Compare in constant time and bound the timestamp to reject replays.

import crypto from "node:crypto";

function verify(secret, header, rawBody) {
  const [t, v1] = header.split(",").map((p) => p.split("=")[1]);
  const expected = crypto
    .createHmac("sha256", secret)
    .update(t + "." + rawBody)
    .digest("hex");
  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(v1, "hex");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Payloads carry only public identifiers (ids, slugs) — fetch the full record from the read API. Headers also include X-Vunk-Event and X-Vunk-Delivery (dedupe on the latter).

Errors

One JSON shape for every error — no stacks, no internals. Status codes: 401 (no/invalid key), 403 (missing scope), 404, 429 (rate limited).

{ "error": { "code": "unauthorized", "message": "API key required",
            "requestId": "01H...", "details": { "header": "X-API-Key" } } }

Get a key

Access is invite-only while we onboard founding companies. A company owner or admin issues API keys from the company account.