RevenueHogdocs
API

Paywall management API

Read and edit paywall entitlements, SKU defaults and A/B experiments through GET and PUT /api/mobile/paywall, with every operation, field and error message.

/api/mobile/paywall is the management API the RevenueHog mobile app and the revenuehog-experiments skill use to edit paywall entitlements and experiments. The web dashboard enforces the same rules through the same code.

Auth is your session (cookie or Authorization: Bearer <session token>), rate limited to 120 requests per minute per user. An app outside your workspace returns 404 Unknown app.

Read state

GET/api/mobile/paywall?appId={appId}Session

Returns entitlements, experiments (with cheap per-variant assigned and exposed counts) and catalog, the mirror of the app's App Store Connect products. Add &experimentId=<id> for full results (see Experiments); that runs a bounded scan, so do not poll it.

Mutate

PUT/api/mobile/paywallSession

The body is a JSON object with appId and op. A successful mutation returns the refreshed overview plus catalog, and warnings when there are any (for example a product ID the catalog has not seen, which is a warning, not an error).

opFields
upsertEntitlementkey, skus (ordered [{ productId, kind }], max 20)
deleteEntitlementkey
createExperimententitlement, name, durationDays, targetExposures?, variants ([{ key, weightPct, skus, isControl }])
updateExperimentexperimentId, plus any of entitlement, name, durationDays, targetExposures, variants, endsAt, weights ({ variantKey: pct })
refreshCatalognone. Re-pulls products from App Store Connect.
start, pause, resume, deleteexperimentId
concludeexperimentId, winnerKey, promote?
{
  "appId": "…", "op": "createExperiment",
  "entitlement": "pro", "name": "Monthly first", "durationDays": 14,
  "variants": [
    { "key": "control", "weightPct": 50, "isControl": true, "skus": [] },
    { "key": "monthly_first", "weightPct": 50, "isControl": false,
      "skus": [{ "productId": "app_pro_monthly", "kind": "subscription" },
               { "productId": "app_pro_annual", "kind": "subscription" }] }
  ]
}

Errors

Errors are { "error": "<sentence>" }, written to be shown to a person.

StatusError
400Invalid JSON body, Body must be a JSON object, appId is required, experimentId is required
400op must be one of upsertEntitlement, deleteEntitlement, createExperiment, updateExperiment, refreshCatalog, start, pause, resume, conclude, delete
400Entitlement keys are short lowercase identifiers, like pro, skus must be an ordered list of { productId, kind }, Unknown entitlement, Unknown entitlement key
400Name the experiment (at most 80 characters), Duration must be 1 to 180 days, Target exposures must be a whole count, variants must be a list of { key, weightPct, skus, isControl }
400Variant rule sentences (see Experiments), endsAt must be a future date, weights must map every variant key to a percentage, Pick the winning variant
401unauthorized
404Unknown app, Unknown entitlement, Unknown experiment
409This entitlement has experiments; history keeps it referenced
409Only drafts start, Set a duration before starting, Set the entitlement's default SKUs before starting; the control variant snapshots them, An experiment is already live for this entitlement
409Only running experiments pause, This experiment already ended, Only paused experiments resume, This experiment ended while paused; extend the end date first
409Only live (or ended) experiments conclude, Only drafts delete, Completed experiments are immutable history
409Only weights and the end date are editable while a test is live; changing what a variant shows mid-test corrupts its cohort
409Catalog refresh failures, for example when the workspace has no App Store Connect credentials
429rate_limited

Last updated on

On this page