PAYMENTS

Content Paywall

Gate premium content behind payment links

Human Flow

Product and intent operations with live status + unlock lifecycle.

Create Product

Products (0)

Loading products...

Intent List (0)

Last refresh:

No intents created in this studio session.

Webhook Simulation (Dev)

Enable to apply signed webhook events (pending/settled/failed/expired) and test idempotency behavior.

Timeline

No events yet.

Agent Flow

Shared action cards for paywall product, intent, settlement status, unlock, and data fetch.

Create Product

HTTP endpoint
POST/api/paywall/products
- Only allowlisted Base mainnet USDC config is currently supported by paywall MVP.
- assetData accepts JSON payload that will be returned on unlock.
curl -X POST '/api/paywall/products' \
  -H 'Content-Type: application/json' \
  -d '{"title":"Pro Research Report","slug":"pro-research-report","description":"Unlocks a JSON payload","amount":"5","assetData":{"report":"alpha"}}'
Example success response
{
  "product": {
    "productId": "prod_...",
    "slug": "pro-research-report",
    "amount": "5"
  }
}
Example failure responses
HTTP 409
{
  "error": "Product slug \"pro-research-report\" already exists.",
  "code": "CONFLICT"
}
Recovery: Use a unique slug or omit slug and let server derive one.
HTTP 400
{
  "error": "title is required.",
  "code": "INVALID_PARAMS"
}
Recovery: Provide required title and amount fields.

Create Intent

HTTP endpoint
POST/api/paywall/intents
- buyerId is auto-derived from paywall cookie when omitted.
- checkoutUrl points to /paywall/[slug] detail page.
curl -X POST '/api/paywall/intents' \
  -H 'Content-Type: application/json' \
  -d '{"productSlug":"pro-research-report","expiryMinutes":15}'
Example success response
{
  "intent": {
    "intentId": "intent_...",
    "status": "pending_payment"
  },
  "product": {
    "slug": "pro-research-report"
  },
  "paymentRequestUrl": "https://example.local/api/pay?...",
  "statusUrl": "https://example.local/api/paywall/intents/intent_.../status",
  "unlockUrl": "https://example.local/api/paywall/intents/intent_.../unlock"
}
Example failure responses
HTTP 404
{
  "error": "Product not found.",
  "code": "NOT_FOUND"
}
Recovery: Create/select an existing product first.
HTTP 409
{
  "error": "Product is not active.",
  "code": "PRODUCT_INACTIVE"
}
Recovery: Use an active product.

Poll Intent Status

HTTP endpoint
GET/api/paywall/intents/:intentId/status
- Status endpoint may transition pending intents to settled/expired/failed.
curl -X GET '/api/paywall/intents/:intentId/status'
Example success response
{
  "intent": {
    "intentId": ":intentId",
    "status": "pending_payment",
    "paidAmount": "0"
  },
  "canUnlock": false
}
Example failure responses
HTTP 404
{
  "error": "Intent not found.",
  "code": "NOT_FOUND"
}
Recovery: Provide a valid intent ID.
HTTP 502
{
  "error": "Unable to verify incoming payment events right now.",
  "code": "VERIFIER_UNAVAILABLE"
}
Recovery: Retry polling after verifier recovers.

Request Unlock Grant

HTTP endpoint
POST/api/paywall/intents/:intentId/unlock
- Unlock token is single-use and expires.
curl -X POST '/api/paywall/intents/:intentId/unlock' \
  -H 'Content-Type: application/json' \
  -d '{"buyerId":"buyer_..."}'
Example success response
{
  "intent": {
    "intentId": ":intentId",
    "status": "settled"
  },
  "unlockToken": "eyJ...",
  "expiresAt": "2026-01-01T00:00:00.000Z",
  "assetId": ":assetId",
  "dataUrl": "https://example.local/api/paywall/data/:assetId"
}
Example failure responses
HTTP 409
{
  "error": "Intent is pending_payment. Payment must be settled before unlock.",
  "code": "INTENT_NOT_SETTLED"
}
Recovery: Wait for settled status before requesting unlock.
HTTP 403
{
  "error": "Intent does not belong to this buyer.",
  "code": "FORBIDDEN"
}
Recovery: Use the same buyer session that created the intent.

Fetch Protected Data

HTTP endpoint
GET/api/paywall/data/:assetId
- Successful fetch consumes token and marks intent delivered.
curl -X GET '/api/paywall/data/:assetId' \
  -H 'Authorization: Bearer <unlockToken>'
Example success response
{
  "contentType": "application/json",
  "payload": {
    "report": "alpha"
  }
}
Example failure responses
HTTP 403
{
  "error": "Unlock token already used.",
  "code": "FORBIDDEN"
}
Recovery: Request a new unlock grant for another fetch.
HTTP 401
{
  "error": "Missing unlock token. Use Authorization: Bearer <token>.",
  "code": "UNAUTHORIZED"
}
Recovery: Attach bearer token in Authorization header.