Skip to main content

Subscribe Pay

The Subscribe Pay capability enables recurring subscription billing. A payer agent subscribes to a service plan, and the subscription automatically renews at each billing interval, generating new PaymentIntents and QR codes for each renewal.


Swimlane Diagrams

The following swimlane diagrams trace the full subscription lifecycle across the four parties: Human (Payer), Agent (Buyer), ItPay Protocol, and Channel (Alipay).

1. Subscription Creation & Authorization

The human requests a subscription, the agent creates it, and the human authorizes the first payment via QR.

┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────┐
│ Human │ │ Agent │ │ ItPay │ │ Channel │
│ (Payer) │ │ (Buyer) │ │ Protocol │ │ (Alipay) │
└────┬─────┘ └──────┬───────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ "Subscribe to │ │ │
│ Pro for $29.99"│ │ │
│─────────────────>│ │ │
│ │ │ │
│ │ POST /v1/subscriptions │
│ │──────────────────> │
│ │ │ │
│ │ 201 Created │ │
│ │ { │ │
│ │ status: │ │
│ │ "pending", │ │
│ │ first_payment:│ │
│ │ {qr: {...}} │ │
│ │ } │ │
│ │<──────────────────│ │
│ │ │ │
│ Show QR code │ │ │
│ "Scan to pay │ │ │
│ $29.99" │ │ │
│<─────────────────│ │ │
│ │ │ │
│ Scans QR with │ │ │
│ phone & auths │ │ │
│──────────────────────────────────────────────> │
│ │ │ │
│ │ │ Payment │
│ │ │ confirmed │
│ │ │<────────────────│
│ │ │ │
│ "Payment sent!" │ │ │
│<────────────────────────────────────────────── │
│ │ │ │
│ │ Webhook: │ │
│ │ subscription. │ │
│ │ activated │ │
│ │<──────────────────│ │
│ │ │ │
│ "Pro plan is │ │ │
│ active!" │ │ │
│<─────────────────│ │ │

Interaction Trace — Subscription Creation:

// Step 1 — Agent creates subscription
// REQUEST
POST /v1/subscriptions
Content-Type: application/json
Authorization: Bearer ***

{
"service_id": "01J7XYKZ1A2B3C4D5E6F7G8H9I",
"plan_id": "plan_pro",
"channel": "alipay",
"payer_info": {
"agent_id": "agent_cli_a1b2c3d4",
"human_id": "user_abc_789"
},
"auto_renew": true,
"return_url": "https://summarybot.ai/welcome"
}

// RESPONSE
HTTP/1.1 201 Created
Content-Type: application/json

{
"id": "sub_01J7Y6A7B8C9D0E1F2G3H4I5J",
"status": "pending",
"plan": {
"plan_id": "plan_pro",
"name": "Pro",
"amount": 2999,
"currency": "USD",
"interval": "monthly"
},
"first_payment": {
"payment_intent_id": "pi_01J7Y6A7B8C9D0E1F2G3H4I5K",
"qr": {
"charge_id": "qr_01J7Y6B7C8D9E0F1G2H3I4J5L",
"scan_url": "https://pay.summarybot.ai/qr/qr_01J7Y6B7C8D9E0F1G2H3I4J5L"
},
"status": "pending",
"expires_at": "2026-05-27T09:30:00Z"
},
"current_period": {
"start": "2026-05-27T09:15:00Z",
"end": "2026-06-27T09:15:00Z"
}
}

// Step 2 — Human scans QR, authorizes in Alipay
// (No direct API call — human action in Alipay app)

// Step 3 — Alipay confirms payment to ItPay
// (Internal callback from payment channel)

// Step 4 — ItPay sends activation webhook
// WEBHOOK (sent to agent's webhook endpoint)
{
"id": "evt_01J7Y7A8B9C0D1E2F3G4H5I6J",
"type": "subscription.activated",
"data": {
"subscription_id": "sub_01J7Y6A7B8C9D0E1F2G3H4I5J",
"status": "active",
"current_period": {
"start": "2026-05-27T09:15:00Z",
"end": "2026-06-27T09:15:00Z"
},
"payment_intent_id": "pi_01J7Y6A7B8C9D0E1F2G3H4I5K"
},
"created_at": "2026-05-27T09:16:00Z"
}

2. Auto-Renewal Cycle

When the billing period ends, ItPay automatically creates a new PaymentIntent. The agent auto-pays (if under auto_pay_limit) or shows a new QR code.

┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────┐
│ Human │ │ Agent │ │ ItPay │ │ Channel │
│ (Payer) │ │ (Buyer) │ │ Protocol │ │ (Alipay) │
└────┬─────┘ └──────┬───────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ [Billing period │ │ │
│ ends] │ │ │
│ │ │ Creates new │
│ │ │ PaymentIntent │
│ │ │ for $29.99 │
│ │ │ │
│ │ Webhook: │ │
│ │ subscription. │ │
│ │ renewed │ │
│ │<──────────────────│ │
│ │ │ │
│ │ Checks amount: │ │
│ │ $29.99 <= │ │
│ │ auto_pay_limit │ │
│ │ ($100.00) ✓ │ │
│ │ │ │
│ │ Auto-pay via │ │
│ │ saved channel │ │
│ │──────────────────> │
│ │ │ │
│ │ │ Process payment │
│ │ │────────────────>│
│ │ │ │
│ │ │ Settlement │
│ │ │ confirmed │
│ │ │<────────────────│
│ │ │ │
│ "Auto-renewed: │ │ │
│ $29.99 charged │ │ │
│ — Pro plan │ │ │
│ continues" │ │ │
│<─────────────────│ │ │
│ │ │ │
│ [Service │ │ │
│ delivery │ │ │
│ continues] │ │ │

Interaction Trace — Auto-Renewal:

// Step 1 — ItPay creates renewal PaymentIntent (automatic)
// No explicit API call — ItPay handles internally at period end

// Step 2 — ItPay sends renewal webhook
// WEBHOOK
{
"id": "evt_01J7Y8C9D0E1F2G3H4I5J6K7L",
"type": "subscription.renewed",
"data": {
"subscription_id": "sub_01J7Y6A7B8C9D0E1F2G3H4I5J",
"new_period": {
"start": "2026-06-27T09:15:00Z",
"end": "2026-07-27T09:15:00Z"
},
"payment_intent_id": "pi_01J7Y8C9D0E1F2G3H4I5J6K7M",
"amount": {
"currency": "USD",
"value": 2999
}
},
"created_at": "2026-06-27T09:15:05Z"
}

// Step 3 — Agent fetches payment intent details
// REQUEST
GET /v1/payment_intents/pi_01J7Y8C9D0E1F2G3H4I5J6K7M
Authorization: Bearer ***

// RESPONSE
HTTP/1.1 200 OK
Content-Type: application/json

{
"id": "pi_01J7Y8C9D0E1F2G3H4I5J6K7M",
"amount": { "currency": "USD", "value": 2999 },
"status": "pending",
"qr": {
"charge_id": "qr_01J7Y8D9E0F1G2H3I4J5K6L7N",
"scan_url": "https://pay.summarybot.ai/qr/qr_01J7Y8D9E0F1G2H3I4J5K6L7N"
}
}

// Step 4 — Agent auto-pays (automatically processes payment
// using saved channel credentials, no human QR needed)
// REQUEST (internal — agent sends payment instruction)
POST /v1/payment_intents/pi_01J7Y8C9D0E1F2G3H4I5J6K7M/pay
Authorization: Bearer ***
Content-Type: application/json

{
"channel": "alipay",
"auto": true
}

// RESPONSE
HTTP/1.1 200 OK
Content-Type: application/json

{
"id": "pi_01J7Y8C9D0E1F2G3H4I5J6K7M",
"status": "processing",
"amount": { "currency": "USD", "value": 2999 }
}

3. Failed Renewal with Retries

When a renewal payment fails, the subscription enters past_due with retry attempts and new QR codes.

┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────┐
│ Human │ │ Agent │ │ ItPay │ │ Channel │
│ (Payer) │ │ (Buyer) │ │ Protocol │ │ (Alipay) │
└────┬─────┘ └──────┬───────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ [Current period │ │ │
│ ends] │ │ │
│ │ │ Creates new │
│ │ │ PaymentIntent │
│ │ │ │
│ │ Webhook: │ │
│ │ subscription. │ │
│ │ renewed │ │
│ │<──────────────────│ │
│ │ │ │
│ │ Auto-pay │ │
│ │ attempt fails │ │
│ │──────────────────> │
│ │ │ │
│ │ │ Insufficient │
│ │ │ funds / decline│
│ │ │ │
│ │ Webhook: │ │
│ │ subscription. │ │
│ │ past_due │ │
│ │<──────────────────│ │
│ │ │ │
│ "Payment issue: │ │ │
│ scan to retry │ │ │
│ $29.99" │ │ │
│<─────────────────│ │ │
│ │ │ │
│ ── Retry #1 ── │ │ │
│ │ │ │
│ Scans new QR │ │ │
│──────────────────────────────────────────────> │
│ │ │ │
│ │ │ Payment fails │
│ │ │ again │
│ │ │ │
│ ── Retry #2 ── │ │ │
│ │ │ │
│ Scans new QR │ │ │
│──────────────────────────────────────────────> │
│ │ │ │
│ │ │ Payment │
│ │ │ confirmed! │
│ │ │<────────────────│
│ │ │ │
│ │ Webhook: │ │
│ │ subscription. │ │
│ │ renewed (paid) │ │
│ │<──────────────────│ │
│ │ │ │
│ "Payment │ │ │
│ successful! │ │ │
│ Pro plan │ │ │
│ restored" │ │ │
│<─────────────────│ │ │

Interaction Trace — Failed Renewal:

// Step 1 — ItPay detects auto-pay failure, sends past_due webhook
// WEBHOOK
{
"id": "evt_01J7Y9A0B1C2D3E4F5G6H7I8J",
"type": "subscription.past_due",
"data": {
"subscription_id": "sub_01J7Y6A7B8C9D0E1F2G3H4I5J",
"status": "past_due",
"retry_count": 1,
"max_retries": 3,
"next_retry_at": "2026-06-28T09:15:00Z",
"payment_intent_id": "pi_01J7Y8C9D0E1F2G3H4I5J6K7M",
"amount": { "currency": "USD", "value": 2999 }
},
"created_at": "2026-06-27T09:15:30Z"
}

// Step 2 — Agent shows new QR to human
// (Agent fetches PaymentIntent which returns a fresh QR)

// Step 3 — Human scans QR, retries payment
// (Human action in Alipay app — may succeed or fail)

// Step 4 — On success, ItPay sends renewed webhook
// WEBHOOK
{
"id": "evt_01J7Y9B1C2D3E4F5G6H7I8J9K",
"type": "subscription.renewed",
"data": {
"subscription_id": "sub_01J7Y6A7B8C9D0E1F2G3H4I5J",
"status": "active",
"new_period": {
"start": "2026-06-27T09:30:00Z",
"end": "2026-07-27T09:30:00Z"
},
"payment_intent_id": "pi_01J7Y8C9D0E1F2G3H4I5J6K7M",
"amount": { "currency": "USD", "value": 2999 }
},
"created_at": "2026-06-27T09:30:05Z"
}

4. Cancellation Flow

The human or agent can cancel a subscription at any time. The subscription stops renewing and enters the cancelled state.

┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────┐
│ Human │ │ Agent │ │ ItPay │ │ Channel │
│ (Payer) │ │ (Buyer) │ │ Protocol │ │ (Alipay) │
└────┬─────┘ └──────┬───────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ "Cancel my │ │ │
│ subscription" │ │ │
│─────────────────>│ │ │
│ │ │ │
│ │ POST │ │
│ │ /v1/subscriptions│ │
│ │ /:id/cancel │ │
│ │──────────────────> │
│ │ │ │
│ │ 200 OK │ │
│ │ { │ │
│ │ status: │ │
│ │ "cancelled", │ │
│ │ cancelled_at: │ │
│ │ "2026-07-15..."│ │
│ │ } │ │
│ │<──────────────────│ │
│ │ │ │
│ │ Webhook: │ │
│ │ subscription. │ │
│ │ cancelled │ │
│ │<──────────────────│ │
│ │ │ │
│ "Subscription │ │ │
│ cancelled. │ │ │
│ Access ends │ │ │
│ Jul 27" │ │ │
│<─────────────────│ │ │
│ │ │ │
│ [If cancelled │ │ │
│ mid-period: │ │ │
│ service valid │ │ │
│ until period │ │ │
│ end, no refund]│ │ │

Interaction Trace — Cancellation:

// Step 1 — Agent cancels the subscription
// REQUEST
POST /v1/subscriptions/sub_01J7Y6A7B8C9D0E1F2G3H4I5J/cancel
Authorization: Bearer ***
Content-Type: application/json

{}

// RESPONSE
HTTP/1.1 200 OK
Content-Type: application/json

{
"id": "sub_01J7Y6A7B8C9D0E1F2G3H4I5J",
"status": "cancelled",
"cancelled_at": "2026-07-15T10:00:00Z",
"current_period": {
"start": "2026-06-27T09:15:00Z",
"end": "2026-07-27T09:15:00Z"
},
"note": "Service continues until end of current billing period"
}

// Step 2 — ItPay sends cancellation webhook
// WEBHOOK
{
"id": "evt_01J7Y9C2D3E4F5G6H7I8J9K0L",
"type": "subscription.cancelled",
"data": {
"subscription_id": "sub_01J7Y6A7B8C9D0E1F2G3H4I5J",
"status": "cancelled",
"cancelled_at": "2026-07-15T10:00:00Z",
"effective_end": "2026-07-27T09:15:00Z"
},
"created_at": "2026-07-15T10:00:05Z"
}

Endpoint

MethodEndpointDescription
POST/v1/subscriptionsCreate a new subscription

Request Fields

FieldTypeRequiredDescription
service_idstring (UUIDv7)yesThe ServiceManifest id
plan_idstringyesPlan identifier from the manifest's pricing.subscription array
channelstringyesDefault payment channel for recurring charges
payer_infoobjectyesPayer identification
payer_info.agent_idstringyesPaying agent identifier
payer_info.human_idstringnoHuman user identifier
auto_renewbooleannoWhether to auto-renew at the end of each billing period (default: true)
return_urlstring (URL)noRedirect URL for the initial payment checkout
metadataobjectnoArbitrary key-value data (max 4 KB)

Request Example

POST /v1/subscriptions
Content-Type: application/json
Authorization: Bearer sk_inst_a1b2c3d4

{
"service_id": "01J7XYKZ1A2B3C4D5E6F7G8H9I",
"plan_id": "plan_pro",
"channel": "alipay",
"payer_info": {
"agent_id": "agent_cli_a1b2c3d4",
"human_id": "user_abc_789"
},
"auto_renew": true,
"return_url": "https://summarybot.ai/welcome",
"metadata": {
"referral": "campaign_q2_2026",
"onboarding_session": "sess_onboard_001"
}
}

Response Example

HTTP/1.1 201 Created
Content-Type: application/json

{
"id": "sub_01J7Y6A7B8C9D0E1F2G3H4I5J",
"service_id": "01J7XYKZ1A2B3C4D5E6F7G8H9I",
"plan_id": "plan_pro",
"status": "pending",
"plan": {
"plan_id": "plan_pro",
"name": "Pro",
"amount": 2999,
"currency": "USD",
"interval": "monthly",
"features": [
"500 summaries/mo",
"Priority support",
"API access"
]
},
"current_period": {
"start": "2026-05-27T09:15:00Z",
"end": "2026-06-27T09:15:00Z"
},
"auto_renew": true,
"first_payment": {
"payment_intent_id": "pi_01J7Y6A7B8C9D0E1F2G3H4I5K",
"amount": {
"currency": "USD",
"value": 2999
},
"qr": {
"charge_id": "qr_01J7Y6B7C8D9E0F1G2H3I4J5L",
"scan_url": "https://pay.summarybot.ai/qr/qr_01J7Y6B7C8D9E0F1G2H3I4J5L"
},
"status": "pending",
"expires_at": "2026-05-27T09:30:00Z"
},
"metadata": {
"referral": "campaign_q2_2026",
"onboarding_session": "sess_onboard_001"
},
"created_at": "2026-05-27T09:15:00Z",
"updated_at": "2026-05-27T09:15:00Z"
}

State Machine

┌───────────┐
│ pending │
└─────┬─────┘
│ initial payment confirmed

┌───────────┐
│ active │
└─────┬─────┘
┌────┴────┐
│ │
▼ ▼
┌─────────┐ ┌──────────┐
│ past_due│ │cancelled │
└────┬────┘ └──────────┘


┌──────────┐
│ expired │
└──────────┘

Transitions

FromToTrigger
pendingactiveInitial payment is confirmed
activepast_dueRenewal payment fails or is pending beyond grace period
past_dueactiveRetried renewal payment succeeds
activecancelledPayer or service owner cancels the subscription
past_duecancelledPayer cancels while past due
past_dueexpiredGrace period ends without payment
activeexpiredEnd of term without auto-renew

Renewal Flow

When a subscription renews:

  1. A new PaymentIntent is created for the renewal amount.
  2. A new QRCharge is generated (if the channel requires QR).
  3. A subscription.renewed webhook event is sent.
  4. If auto_renew is false, the subscription expires at the end of the current period.
// Webhook: subscription.renewed
{
"id": "evt_01J7Y8C9D0E1F2G3H4I5J6K7L",
"type": "subscription.renewed",
"data": {
"subscription_id": "sub_01J7Y6A7B8C9D0E1F2G3H4I5J",
"new_period": {
"start": "2026-06-27T09:15:00Z",
"end": "2026-07-27T09:15:00Z"
},
"payment_intent_id": "pi_01J7Y8C9D0E1F2G3H4I5J6K7M",
"amount": {
"currency": "USD",
"value": 2999
}
},
"created_at": "2026-06-27T09:15:05Z"
}

Key Behaviors

  • First payment QR: The response includes a first_payment object with a QR code for the initial payment. The subscription transitions to active only after this payment is confirmed.
  • Auto-renew: When auto_renew is true, a new PaymentIntent is automatically created shortly before the current period ends. The agent can choose to auto-pay (if under auto_pay_limit) or show a new QR code.
  • Past due handling: If a renewal payment fails, the subscription enters past_due. The service retries payment after a configurable interval (default: 3 retries over 7 days). After the grace period, the subscription expires.
  • Plan changes: To change plans, cancel the existing subscription and create a new one with the desired plan_id. Proration is not handled at the protocol level.

Webhook Events

EventDescription
subscription.createdSubscription was created and is pending initial payment
subscription.activatedInitial payment confirmed, subscription is active
subscription.renewedSubscription has been renewed for a new period
subscription.past_dueRenewal payment failed, subscription is past due
subscription.cancelledSubscription was cancelled
subscription.expiredSubscription expired (end of term without renewal)

Next Steps