For a step-by-step integration guide, see External Integration Guide.
/api/openverb/healthVerify connectivity. No auth required.
{ "status": "ok", "message": "PaywallOS Service is active" }/api/openverb/check-verbQuery: appId, userId, verb, tier (optional, must be slug e.g. free/pro, not UUID). Returns { allowed, verb, tier, requiredTier?, message }.
/api/openverb/checkCheck if a user has access to perform a specific verb action.
{
"verbId": "export_data",
"actor": { "type": "user", "id": "user_123" },
"context": { "tenantId": "app_456", "planId": "pro" }
}{
"ok": true,
"receipt": {
"executionId": "...",
"verbId": "export_data",
"status": "ok",
"actorId": "user_123",
"tenantId": "app_456"
}
}{
"denied": true,
"reason": {
"code": "tier_required",
"message": "Upgrade to Pro"
},
"upsell": {
"suggestedPlanId": "pro",
"cta": "Upgrade Now"
}
}/api/openverb/libraryRetrieve the complete OpenVerb library for your app.
?appId=app_456
{
"namespace": "myapp.core",
"version": "1.0.0",
"verbs": [
{
"id": "verb_789",
"name": "export_data",
"category": "file_system",
"description": "Export user data to CSV"
}
]
}/api/openverb/user-tierGet the tier for a user in your app. Use userId as your app's external user ID. Returns the tier slug (e.g. free, pro) based on their subscription.
?appId=app_456&userId=user_123
{
"tier": "pro"
}/api/end-users|POST/api/end-usersList or create end-users (customers) for your organization. Requires dashboard auth.organizationId is passed as a query param (GET) or in the body (POST).
{
"organizationId": "org_xxx",
"externalUserId": "user_123",
"email": "user@example.com",
"metadata": { "name": "Display Name" }
}/api/stripe/checkoutCreate a Stripe Checkout session. Use appId + API key (no organizationId needed).
POST /api/stripe/checkout
Authorization: Bearer <your_api_key>
{
"appId": "your-app-uuid",
"planId": "tier-uuid",
"userId": "your-user-id",
"successUrl": "https://yourapp.com/success",
"cancelUrl": "https://yourapp.com/cancel"
}{ "sessionId": "cs_...", "url": "https://checkout.stripe.com/..." }Redirect the user to url. Get tier IDs from the app page in the dashboard.
/api/stripe/customer-portalGet a Stripe Customer Portal URL so your end-users can cancel their subscription, update payment methods, or view invoices. Use appId + API key (no organizationId needed).
POST /api/stripe/customer-portal
Authorization: Bearer <your_api_key>
{
"appId": "your-app-uuid",
"userId": "your-user-id",
"returnUrl": "https://yourapp.com/account"
}{ "url": "https://billing.stripe.com/..." }Redirect the user to url. Stripe hosts the portal — users can cancel (at end of period), update payment, view invoices. returnUrl is where they land after closing the portal.
/api/openverb/entitlementsGet all verbs a user is entitled to access based on their tier.
?userId=user_123&appId=app_456
{
"userId": "user_123",
"tier": "pro",
"entitlements": [
{
"verbId": "export_data",
"verbName": "export_data",
"usageLimit": {
"limit": 100,
"used": 45,
"period": "monthly"
}
}
]
}All API requests require authentication using your API key in the Authorization header:
Authorization: Bearer pk_live_...
Copy lib/paywall-sdk.ts from the PaywallOS repo. It provides initPaywallOS and usePaywallOS:
import { initPaywallOS, usePaywallOS } from '@/lib/paywall-sdk'
// One-time init (scans DOM for verb= attributes)
initPaywallOS(apiKey, appId, userId, userTier)
// Or use the hook for programmatic checks
const { checkVerb } = usePaywallOS(apiKey, appId, userId, userTier)
const response = await checkVerb('export_data')
if (response.ok) { /* allowed */ }
else if (response.denied) { /* response.reason?.message */ }