Subscription linking overview

Subscribers should never hit the paywall. We support two transports for
proving "this MCP install belongs to one of our paying subscribers" — pick
whichever fits your stack.

Both feed the same SubscriptionLink table and end up stamping
tier="subscriber" on the install's OAuth tokens. The meter check in
get_article early-returns for subscriber-tier requests — PAYWALL articles
serve normally, no MeterEvent written.

Where readers see it

Two entry points in the UI:

  • At install time — the consent screen at /authorize shows an
    "Already a subscriber? Connect my subscription" button above the
    free-tier email form.
  • At paywall trip — the paywalled payload includes a subscriberLinkUrl
    so the agent renders an "Already a subscriber? Link your account →"
    secondary CTA inline in chat.

Both buttons hit /api/oauth/start-subscription-link, which mints an
installToken, persists a SubscriptionLink row, and 302s the reader to
your subscriptionConnectUrl.

The two transports

  • Flip-Pay cookie — zero publisher dev work, requires the
    vanity host CNAME.
  • HMAC webhook — for publishers not on Flip-Pay; ~1
    afternoon of dev time on your side.

Cancellation

Cancellation signals (Flip-Pay JWT flips to non-subscriber, webhook
status="cancelled") downgrade existing tokens in place. No need to wait
for token expiry.

Adding another vendor

Want to support Memberful, Pico, Piano, WordPress paid memberships, or
something else? The pattern is: add a nullable Publisher.<vendor>Audience
field, a verifier under lib/oauth/, and a branch in
/api/oauth/connect-mcp that picks the right verifier based on which
field is set. The SubscriptionLink flow is vendor-agnostic — ping us
and we'll add it.