Verune API
The API answers questions about AI agent assets (MCP servers, plugins, skills) from Verune's continuously crawled corpus. Verune never runs in your traffic path and never blocks anything. Decisions are advice that your own gateway, proxy, installer or CI job enforces.
Authentication
Create a key on your API keys page (Pro and Team plans, workspace owner only). Send it on every request:
Authorization: Bearer vrn_...
Keys are shown once and stored only as a hash. A key stops working when it is revoked, or when its creator leaves the workspace.
Errors and limits
Errors use RFC 9457 problem JSON with a stable type. Validation errors list {"path","code"} pairs and never repeat your input.
401 unauthorized: missing, unknown or revoked key.403 plan-required: your plan does not include this endpoint.429 rate-limitedorquota-exceeded: wait forRetry-Afterseconds.503 intel-unavailable: temporary; retry with backoff.
Searches, asset reads and lookups count against your monthly search allowance (one per lookup subject). A report counts once per asset per month. Request bodies are limited to 256 KiB.
Read endpoints (Pro and Team)
GET /api/v1/assets?q=filesystem&limit=10
GET /api/v1/assets/{assetId}
GET /api/v1/assets/{assetId}/report
POST /api/v1/lookup {"subjects": [ {subject}, ... up to 100 ]}
Lookup returns, per subject in request order, how it resolved and the current risk level:
{"items":[{"subjectKey":"npm:@acme/mcp-files@2.1.0",
"resolution":{"state":"RESOLVED","assetId":"…","revisionId":"…","versionSpecified":true,"candidateCount":1},
"asset":{…public summary…},"riskLevel":"MEDIUM"}]}
Subjects
A subject names one asset. Exactly one of these shapes:
{"kind":"npm","name":"@acme/mcp-files","version":"2.1.0"} version optional
{"kind":"pypi","name":"acme-mcp","version":"0.4.0"} version optional
{"kind":"mcp_registry","name":"io.github.acme/files","version":"1.0.0"}
{"kind":"repository","url":"https://github.com/acme/mcp-files"}
{"kind":"sha256","value":"<64 hex characters of the artifact>"}
{"kind":"remote_endpoint","url":"https://mcp.example.com/sse"}
{"kind":"asset","assetId":"…","revisionId":"…"} revisionId optional
Resolution states: RESOLVED, UNKNOWN (not in the corpus), AMBIGUOUS (several assets match, for example a monorepo), UNKNOWN_VERSION (the asset is known but that version is not). A missing version means the latest known revision.
Team endpoints
GET /api/v1/inventory?after={id}&limit=100
POST /api/v1/inventory {"subject":{…},"label":"prod"}
DELETE /api/v1/inventory/{id}
POST /api/v1/inventory/{id}/approve {"revisionId":"…"}
DELETE /api/v1/inventory/{id}/approve
GET /api/v1/alerts?after={seq}&limit=100
GET /api/v1/policy
PUT /api/v1/policy {policy document}
POST /api/v1/decisions {"subject":{…},"context":{"client":"ci-gate"}}
GET | PUT | DELETE /api/v1/webhook PUT body: {"url":"https://…"}
POST /api/v1/webhook/rotate-secret
Register an unversioned subject to follow upstream releases, or a versioned one to watch a pinned install. Verune re-checks the inventory every 15 minutes and records alerts: NEW_REVISION, RISK_LEVEL_CHANGED, FINDINGS_CHANGED, UPSTREAM_REMOVED, RESOLUTION_CHANGED, DRIFT_FROM_APPROVED. Read them with GET /api/v1/alerts, passing the last seq you processed as after.
Policy document
Every key is required. Each PUT creates a new immutable version; until you store one, this default applies as version 0:
{"schema":"verune-policy/v1",
"onUnknownAsset":"WARN","onAmbiguous":"WARN","onUnknownVersion":"WARN",
"onUnscored":"WARN","onRemovedUpstream":"DENY",
"denyAtOrAbove":"CRITICAL","warnAtOrAbove":"HIGH",
"denyFindingTypes":[],"warnFindingTypes":[],
"inventory":{"mode":"OFF","onDriftFromApproved":"WARN"},
"allow":[],"deny":[]}
- Actions are
ALLOW,WARNorDENY. Thresholds areLOW,MEDIUM,HIGH,CRITICALorNEVER. inventory.modeWARNorDENYapplies that action to anything not in your inventory.onDriftFromApprovedapplies when the resolved revision differs from the one you approved.allowanddenyhold up to 500 entries each:{"subject":{…},"pinRevisionId":null,"note":"why"}. A deny match wins over everything; an allow match wins over every other check. An unversioned entry matches every version.
Decisions
{"decisionId":"…","decision":"WARN",
"reasons":[{"code":"RISK_LEVEL","action":"WARN","detail":"HIGH"},
{"code":"VERSION_UNSPECIFIED","action":"ALLOW","detail":null}],
"subject":{…},"subjectKey":"npm:@acme/mcp-files@*",
"asset":{"assetId":"…","name":"…","kind":"MCP_SERVER"},
"revision":{"revisionId":"…","version":"2.1.0","contentHash":"…"},
"risk":{"level":"HIGH","modelVersion":"v5","assessedAt":"…"},
"policyVersion":3,"evaluatedAt":"…","dataAsOf":"…","cacheTtlSeconds":300,"advisory":true}
The decision is the most severe action among the reasons. Reason codes: EXPLICIT_DENY, EXPLICIT_ALLOW, UNKNOWN_ASSET, AMBIGUOUS_SUBJECT, UNKNOWN_VERSION, REMOVED_UPSTREAM, UNSCORED, RISK_LEVEL, FINDING_TYPE, NOT_IN_INVENTORY, DRIFT_FROM_APPROVED, VERSION_UNSPECIFIED.
Caching. Honor Cache-Control: private, max-age: 300 seconds for resolved subjects, 60 otherwise. Cache by subject key.
Failure handling. If the API is unreachable or returns 5xx, Verune gives no decision. We recommend failing open with a warning in your client, so a Verune outage never blocks your own users. Fail closed only where your risk tolerance requires it.
Risk comes from automated static analysis of published artifacts under the active rule model. It is not human-reviewed and artifacts are never executed.
Webhooks
PUT /api/v1/webhook with an https URL on port 443. The response to the first PUT (and to rotate-secret) contains the signing secret once. Verune posts batches of up to 50 alerts:
POST {your url}
Content-Type: application/json
Verune-Delivery: <uuid>
Verune-Signature: t=1760000000,v1=<hex>
{"workspaceId":"…","alerts":[{alert}, …]}
To verify: compute HMAC-SHA256 with the secret over t + "." + raw body, compare it to v1 in constant time, and reject timestamps older than five minutes. Reply with any 2xx to acknowledge. Failures retry with exponential backoff up to an hour; after 20 consecutive failures the webhook is disabled until you PUT it again. Alerts are never lost: GET /api/v1/alerts always has them.