SDK Reference
Early Draft
This specification is at an early draft stage. Ideas are open for change and debate. A lot of the content was developed with the help of Claude AI.
SDK Reference
Installation
# Python
pip install vera-publisher-sdk
# Node.js / Next.js
npm install @vera-browser/publisher-sdk
# PHP (Composer)
composer require vera-browser/publisher-sdk
# WordPress
wp plugin install vera-browser-publisher
Unified Gateway
The central SDK entry point: automatic routing to VRP or standard.
from vera_sdk import VeraGateway, PublisherConfig
config = PublisherConfig(
publisher_key = "spiegel",
domain = "some-publisher.news",
vera_api_key = "vera_live_...",
)
gateway = VeraGateway(config)
def article_view(request, slug):
article = get_article(slug)
return gateway.handle(request, article)
Python / Django
from vera_sdk import VeraContext, AccessOptions, VeraMiddleware
# settings.py
MIDDLEWARE = ["vera_sdk.middleware.VeraMiddleware", ...]
# views.py
def article_view(request, slug):
article = Article.objects.get(slug=slug)
requestor = request.vera.requestor_type # set by VeraMiddleware
if requestor == "vera_human":
vera = request.vera
if vera.has_subscription("spiegel"):
return HttpResponse(render_vera_template(article), status=200)
r = HttpResponse(render_vera_preview(article), status=402)
r["X-Vera-Access"] = AccessOptions.choice(
ppr_price=0.49, subscription_key="spiegel"
).to_json()
return r
if requestor == "ai_agent":
return JsonResponse({"error": "licensed_content"}, status=402)
if requestor == "standard_browser":
return render_standard(request, article)
return HttpResponse(status=403)
Node.js / Next.js: Edge Middleware
// middleware.ts
import { VeraEdgeMiddleware } from "@vera-browser/publisher-sdk/edge";
export const config = { matcher: "/article/:path*" };
export default VeraEdgeMiddleware({
publisherKey: "spiegel",
apiKey: process.env.VERA_API_KEY!,
onVeraAuthenticated: async (request, vera) => {
if (vera.hasSubscription("spiegel")) {
return rewriteTo(request, "?vera=clean");
}
return vera.paymentRequired({
options: [
{ type: "ppr", price: 0.49, currency: "EUR" },
{ type: "subscription", key: "spiegel" },
{ type: "ad_supported" },
],
});
},
onVeraAnonymous: async (request) =>
rewriteTo(request, "?vera=clean&auth=required"),
onAiAgent: async () =>
new Response(JSON.stringify({ error: "licensed_content" }), { status: 402 }),
onUnknownBot: async () => new Response("Forbidden", { status: 403 }),
onStandardBrowser: async (request) => NextResponse.next(),
});
PHP / WordPress Plugin
<?php
define('VERA_PUBLISHER_KEY', 'spiegel');
add_action('template_redirect', 'vera_gateway');
function vera_gateway(): void {
if (!is_singular('post')) return;
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
// Vera browser
if (str_starts_with($ua, 'Vera/')) {
$token = $_SERVER['HTTP_X_VERA_TOKEN'] ?? null;
$vera = vera_validate_token($token, get_site_url());
if ($vera && in_array(VERA_PUBLISHER_KEY, $vera['subscriptions'] ?? [])) {
add_filter('template_include', fn() => VERA_TEMPLATE_PATH);
return;
}
http_response_code(402);
header('X-Vera-Access: ' . vera_access_options_json());
add_filter('template_include', fn() => VERA_PREVIEW_TEMPLATE_PATH);
return;
}
// AI agent
if (vera_is_agent($ua)) {
http_response_code(402);
header('Content-Type: application/json');
echo json_encode(['error' => 'licensed_content']);
exit;
}
// Unknown bot
if (vera_is_unknown_bot($ua)) { http_response_code(403); exit; }
// Standard browser: continue normally
}
function vera_is_agent(string $ua): bool {
$agents = ['GPTBot', 'ClaudeBot', 'anthropic-ai', 'PerplexityBot', 'CCBot', 'Googlebot'];
foreach ($agents as $a) {
if (stripos($ua, $a) !== false) return true;
}
return false;
}
?>
API Reference: VeraContext
class VeraContext:
# Classification
requestor_type: RequestorType # VERA_HUMAN | AI_AGENT | STANDARD_BROWSER | UNKNOWN_BOT
is_vera: bool
is_agent: bool
mode: str # "standard" | "vera_anonymous" | "vera_authenticated" | "vera_invalid"
# Vera-specific (only when is_vera=True)
subscriptions: list[str] # Active subscription keys
ppr_balance: float # Pay-per-read balance in EUR
consent_scope: list[str] # Granted consent categories
network_verified: bool # Network verification signal valid
human_score: float # 0.0–1.0
client_version: str # e.g. "1.2.4"
def has_subscription(self, publisher_key: str) -> bool: ...
def has_consent(self, scope: str) -> bool: ...
def is_human(self, threshold: float = 0.85) -> bool: ...
class AccessOptions:
@staticmethod
def ppr(price: float, currency: str = "EUR") -> "AccessOptions": ...
@staticmethod
def subscription(key: str) -> "AccessOptions": ...
@staticmethod
def choice(ppr_price: float, subscription_key: str,
ad_supported: bool = False) -> "AccessOptions": ...
def to_json(self) -> str: ...