Subscription Management¶
Complete guide to managing subscriptions, upgrades, renewals, and billing in the Reserva platform.
Overview¶
The subscription system provides flexible tiered plans with support for:
- Plan Upgrades - Change to a higher/lower tier with prorated billing
- Subscription Renewals - Extend subscription period with same plan
- Usage Tracking - Monitor consumption against plan limits
- Payment History - View all subscription-related payments
- Scheduled Downgrades - Plan downgrades applied at next billing cycle
- Cancellations - End subscription with grace period
Key Concepts:
- Upgrade = Plan changes (FREE→PRO→ENTERPRISE), period stays the same
- Renewal = Period extends, plan stays the same
- Prorated Billing = Upgrades charge difference for remaining period
- Full Period Billing = Renewals charge full amount for new period
Available Plans¶
Get the list of available subscription plans with pricing and limits.
Endpoint¶
Authentication: Required (JWT token)
Response¶
{
"plans": [
{
"plan_type": "FREE",
"display_name": "Free Plan",
"description": "Perfect for getting started",
"price": {
"monthly": 0,
"quarterly": 0,
"yearly": 0,
"currency": "IDR"
},
"limits": {
"max_outlets": 1,
"max_staff_per_outlet": 5,
"max_appointments_per_month": 100,
"max_services": 10
},
"features": [
"Basic booking management",
"Email notifications",
"Customer portal"
]
},
{
"plan_type": "PRO",
"display_name": "Pro Plan",
"description": "For established businesses",
"price": {
"monthly": 599000,
"quarterly": 1617300,
"yearly": 6468000,
"currency": "IDR"
},
"limits": {
"max_outlets": 10,
"max_staff_per_outlet": 50,
"max_appointments_per_month": 2000,
"max_services": 50
},
"features": [
"Everything in Free",
"API access",
"Waitlist management",
"Loyalty programs",
"Priority support"
]
},
{
"plan_type": "ENTERPRISE",
"display_name": "Enterprise Plan",
"description": "For large organizations",
"price": {
"monthly": 1499000,
"quarterly": 4047300,
"yearly": 16188000,
"currency": "IDR"
},
"limits": {
"max_outlets": -1,
"max_staff_per_outlet": -1,
"max_appointments_per_month": -1,
"max_services": -1
},
"features": [
"Everything in Pro",
"Unlimited everything",
"Dedicated account manager",
"Custom integrations",
"SLA guarantee"
]
}
]
}
Note: -1 means unlimited for that limit.
Get Current Subscription¶
Retrieve detailed information about the tenant's current subscription.
Endpoint¶
Authentication: Required (JWT token)
Response¶
{
"subscription_id": "67890abcdef1234567890123",
"tenant_id": "12345abcdef67890abcdef12",
"plan_type": "PRO",
"billing_cycle": "monthly",
"status": "active",
"current_period_start": "2025-09-07T00:00:00Z",
"current_period_end": "2025-10-07T00:00:00Z",
"next_billing_date": "2025-10-07T00:00:00Z",
"is_trial": false,
"trial_ends_at": null,
"auto_renew": true,
"plan_details": {
"display_name": "Pro Plan",
"price": 599000,
"currency": "IDR",
"limits": {
"max_outlets": 10,
"max_staff_per_outlet": 50,
"max_appointments_per_month": 2000,
"max_services": -1
}
},
"usage": {
"outlets": 3,
"staff": 12,
"appointments_this_month": 234
},
"scheduled_changes": null
}
Subscription Statuses:
active- Subscription is active and paidpast_due- Payment failed, grace period activecanceled- Subscription canceled, will expire at period endexpired- Subscription period ended without renewal
Subscription Upgrade¶
Change to a different plan tier with prorated billing for the remaining period.
Endpoint¶
Authentication: Required (JWT token)
Request Body¶
Parameters:
target_plan(required) - Target plan:free,pro,enterprisebilling_period(optional) -monthlyoryearly(defaults to current period)prorate_charges(optional) - Whether to prorate charges (default:true)
Response¶
{
"status": "payment_pending",
"message": "Invoice created successfully. Please complete payment to activate your subscription upgrade.",
"subscription": {
"id": "507f1f77bcf86cd799439011",
"plan": "free",
"status": "active",
"current_period_end": "2025-02-15"
},
"invoice": {
"id": "507f1f77bcf86cd799439012",
"invoice_number": "INV-2025-001",
"amount": "249950",
"currency": "IDR",
"due_date": "2025-01-18",
"status": "pending",
"paper_invoice_id": "PI-123456",
"paper_invoice_url": "https://stg-v2.paper.id/abc123",
"paper_pdf_url": "https://stg-v2.paper.id/pdf/xyz789",
"paper_payment_url": "https://payper.id/short123"
},
"upgrade_details": {
"from_plan": "free",
"to_plan": "pro",
"prorated_amount": "249950",
"days_remaining": 15,
"total_days": 30,
"billing_period": "monthly",
"prorated": true
},
"next_steps": [
"1. Open the payment URL to complete payment",
"2. Choose your preferred payment method",
"3. Your subscription will be upgraded automatically upon payment confirmation",
"4. You will receive an email confirmation once activated"
]
}
Upgrade Flow (12 Steps)¶
The upgrade process follows this sequence:
- Validation - Check new plan is different from current
- Proration Calculation - Calculate prorated charge for remaining period
- Invoice Generation - Create subscription upgrade invoice
- Paper.id Integration - Generate Paper.id invoice with metadata
- Invoice Creation - Store invoice in database
- Payment URL - Return payment link to client
- Customer Payment - Customer completes payment on Paper.id
- Webhook Received - Paper.id sends payment confirmation
- Invoice Routing - Webhook router identifies as upgrade (no renewal flag)
- Payment Verification - Validate payment amount and status
- Subscription Update - Update subscription to new plan
- Notification - Send upgrade confirmation email
Webhook Routing for Upgrades:
# Webhook receives invoice payment event
if invoice.invoice_type == "SUBSCRIPTION":
if invoice.metadata.get('renewal') == True:
# Route to renewal handler
handle_invoice_payment_for_renewal()
else:
# Route to upgrade handler (NO renewal flag)
handle_invoice_payment_for_subscription()
Important Notes:
- Upgrades are NOT applied immediately upon API call
- Upgrade activates after payment is completed
- Downgrade to lower plan schedules change for next billing cycle
- Prorated amount is calculated:
(new_price - old_price) × (days_remaining / days_in_period) - Invoice expires in 7 days if unpaid
Subscription Renewal¶
Extend subscription period with the same plan (no plan change).
Endpoint¶
Authentication: Required (JWT token)
Request Body¶
Parameters:
subscription_id(required) - Subscription ID to renew
Response¶
{
"status": "payment_pending",
"message": "Renewal invoice created successfully. Please complete payment to continue your subscription.",
"subscription": {
"id": "507f1f77bcf86cd799439011",
"plan": "pro",
"billing_period": "monthly",
"current_period_end": "2025-02-15"
},
"invoice": {
"id": "507f1f77bcf86cd799439012",
"invoice_number": "INV-2025-002",
"amount": "499900",
"currency": "IDR",
"due_date": "2025-02-22",
"paper_invoice_url": "https://stg-v2.paper.id/abc123",
"paper_pdf_url": "https://stg-v2.paper.id/pdf/xyz789",
"paper_payment_url": "https://payper.id/short456"
},
"renewal_details": {
"renewing_plan": "pro",
"billing_period": "monthly",
"renewal_amount": "499900",
"next_period_start": "2025-02-15",
"next_period_end": "2025-03-15"
},
"next_steps": [
"1. Open the payment URL to complete payment",
"2. Your subscription will be extended automatically upon payment confirmation",
"3. You will receive an email confirmation"
]
}
Renewal Flow¶
- Validation - Verify subscription is active and can be renewed
- Invoice Generation - Create renewal invoice with
renewal=truemetadata - Paper.id Integration - Generate Paper.id invoice with renewal flag
- Full Period Charge - Charge full amount for selected billing cycle (no proration)
- Payment URL - Return payment link to client
- Customer Payment - Customer completes payment
- Webhook Received - Paper.id sends payment confirmation
- Invoice Routing - Webhook router identifies renewal flag in metadata
- Subscription Extension - Extend
current_period_endby billing cycle duration - Notification - Send renewal confirmation email
Webhook Routing for Renewals:
# Webhook receives invoice payment event
if invoice.invoice_type == "SUBSCRIPTION":
if invoice.metadata.get('renewal') == True:
# Route to RENEWAL handler (renewal flag present)
handle_invoice_payment_for_renewal()
else:
# Route to upgrade handler
handle_invoice_payment_for_subscription()
Key Differences from Upgrade:
| Aspect | Renewal | Upgrade |
|---|---|---|
| Plan | Stays the same | Changes to new plan |
| Period | Extends by billing cycle | Stays the same |
| Billing | Full period charge | Prorated for remaining days |
| Metadata | renewal: true |
No renewal flag |
| Webhook Handler | handle_invoice_payment_for_renewal() |
handle_invoice_payment_for_subscription() |
| Activation | Period extended on payment | Plan changed on payment |
Subscription Downgrade¶
Schedule a downgrade to a lower-tier plan, effective at next billing cycle.
Endpoint¶
Authentication: Required (JWT token)
Request Body¶
Parameters:
target_plan(required) - Target plan:free,pro,enterprisereason(optional) - Reason for downgrade
Response¶
Returns updated subscription with scheduled downgrade:
{
"id": "507f1f77bcf86cd799439011",
"tenant_id": "507f1f77bcf86cd799439010",
"plan": "pro",
"status": "active",
"billing_period": "monthly",
"current_period_start": "2025-01-15",
"current_period_end": "2025-02-15",
"scheduled_changes": {
"target_plan": "free",
"effective_date": "2025-02-15",
"reason": "Reducing business size",
"scheduled_at": "2025-01-20T10:30:00Z"
},
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-20T10:30:00Z"
}
Important:
- Downgrades are NOT immediate (prevent revenue loss)
- Applied at
current_period_end - No refund for remaining period
- Can be canceled before effective date
Usage Tracking¶
Monitor current usage against subscription plan limits.
Endpoint¶
Authentication: Required (JWT token)
Response¶
{
"subscription_id": "67890abcdef1234567890123",
"plan": "PRO",
"billing_period": {
"start": "2025-09-07T00:00:00Z",
"end": "2025-10-07T00:00:00Z"
},
"usage": {
"outlets": {
"current": 3,
"limit": 10,
"percentage": 30,
"status": "within_limit"
},
"staff": {
"current": 12,
"limit": 50,
"percentage": 24,
"status": "within_limit"
},
"appointments_this_month": {
"current": 234,
"limit": 2000,
"percentage": 11.7,
"status": "within_limit"
},
"services": {
"current": 45,
"limit": -1,
"percentage": 0,
"status": "unlimited"
}
},
"warnings": [],
"upgrade_recommended": false
}
Status Values:
within_limit- Usage below limitapproaching_limit- Usage > 80% of limitat_limit- Usage at 100% of limitexceeded- Usage over limit (blocked)unlimited- No limit for this resource
Cancel Subscription¶
Cancel subscription by immediately downgrading to FREE plan.
Endpoint¶
Authentication: Required (JWT token)
Request Body¶
No request body required.
Response¶
Returns subscription downgraded to FREE:
{
"id": "507f1f77bcf86cd799439011",
"tenant_id": "507f1f77bcf86cd799439010",
"plan": "free",
"status": "active",
"billing_period": "monthly",
"current_period_start": "2025-01-15",
"current_period_end": "2025-02-15",
"metadata": {
"cancelled_at": "2025-01-20T10:30:00Z",
"cancelled_by": "user_id_here",
"previous_plan": "pro"
},
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-20T10:30:00Z"
}
Important:
- Immediately downgrades to FREE plan
- User keeps access with FREE tier limits
- Status stays
active(not blocked) - Previous plan stored in metadata for reference
- Cannot cancel if already on FREE plan
Deactivate Subscription¶
Suspend a subscription (admin action for edge cases).
Endpoint¶
Authentication: Required (JWT token)
Request Body¶
Parameters:
reason(optional) - Reason for deactivation
Response¶
Returns suspended subscription:
{
"id": "507f1f77bcf86cd799439011",
"tenant_id": "507f1f77bcf86cd799439010",
"plan": "pro",
"status": "suspended",
"billing_period": "monthly",
"metadata": {
"deactivated_at": "2025-01-20T10:30:00Z",
"deactivated_by": "admin_user_id",
"deactivation_reason": "Payment fraud detected"
},
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-20T10:30:00Z"
}
Use Cases:
- Admin manually suspending a tenant
- Payment fraud detection
- Terms of service violation
- Temporary account suspension
Important:
- Plan and features are preserved
- Access is blocked (status =
suspended) - Can be reactivated using
/activateendpoint
Activate Subscription¶
Reactivate a suspended subscription.
Endpoint¶
Authentication: Required (JWT token)
Request Body¶
No request body required.
Response¶
Returns activated subscription:
{
"id": "507f1f77bcf86cd799439011",
"tenant_id": "507f1f77bcf86cd799439010",
"plan": "pro",
"status": "active",
"billing_period": "monthly",
"metadata": {
"activated_at": "2025-01-21T09:00:00Z",
"activated_by": "admin_user_id"
},
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-21T09:00:00Z"
}
Use Cases:
- Reactivating after payment issue resolved
- Admin manually reactivating a tenant
- Lifting suspensions after investigation
Important:
- Restores access immediately
- Plan and features unchanged
- Clears deactivation metadata
Payment History¶
Retrieve all subscription-related payments.
Endpoint¶
Authentication: Required (JWT token)
Query Parameters¶
limit(optional) - Results per page (default: 20, max: 100)offset(optional) - Pagination offset (default: 0)status(optional) - Filter by status:completed,pending,failed,refunded
Response¶
Returns array of payment records:
[
{
"id": "507f1f77bcf86cd799439020",
"tenant_id": "507f1f77bcf86cd799439010",
"invoice_id": "507f1f77bcf86cd799439012",
"subscription_id": "507f1f77bcf86cd799439011",
"amount": 499900,
"currency": "IDR",
"status": "completed",
"payment_type": "subscription_renewal",
"payment_method": "paper_id",
"paper_invoice_id": "PI-20250115-ABC123",
"paid_at": "2025-01-15T10:30:00Z",
"created_at": "2025-01-15T10:25:00Z",
"metadata": {
"plan": "pro",
"billing_period": "monthly"
}
},
{
"id": "507f1f77bcf86cd799439021",
"tenant_id": "507f1f77bcf86cd799439010",
"invoice_id": "507f1f77bcf86cd799439013",
"subscription_id": "507f1f77bcf86cd799439011",
"amount": 249950,
"currency": "IDR",
"status": "completed",
"payment_type": "subscription_upgrade",
"payment_method": "paper_id",
"paper_invoice_id": "PI-20250101-DEF456",
"paid_at": "2025-01-01T14:20:00Z",
"created_at": "2025-01-01T14:15:00Z",
"metadata": {
"from_plan": "free",
"to_plan": "pro",
"prorated": true
}
}
]
Webhook Architecture¶
The platform uses Paper.id as the payment gateway, which has a limitation: only one webhook URL per account.
Problem¶
With multiple invoice types (subscription upgrades, renewals, appointments), we need to route webhooks to different handlers but can only register one endpoint.
Solution: Single Endpoint with Intelligent Routing¶
Webhook Endpoint: POST /api/v1/webhooks/paper-invoice
All Paper.id webhook events are sent to this single endpoint, which then routes internally based on:
- Invoice Type (
invoice_typefield) - Metadata Flags (custom data in invoice)
Routing Logic¶
async def handle_paper_invoice_webhook(payload: dict):
"""
Single webhook endpoint that routes to appropriate handler
"""
invoice_data = payload.get('data', {})
invoice_type = invoice_data.get('invoice_type')
metadata = invoice_data.get('metadata', {})
# Route based on invoice type and metadata
if invoice_type == "SUBSCRIPTION":
if metadata.get('renewal') == True:
# Renewal: Period extends, plan stays same
await handle_invoice_payment_for_renewal(invoice_data)
else:
# Upgrade: Plan changes, period stays same
await handle_invoice_payment_for_subscription(invoice_data)
elif invoice_type == "APPOINTMENT":
# Appointment booking payment
await handle_invoice_payment_for_appointment(invoice_data)
else:
# Unknown invoice type
logger.warning(f"Unknown invoice type: {invoice_type}")
raise HTTPException(status_code=400, detail="Unknown invoice type")
Invoice Metadata Structure¶
Subscription Upgrade Invoice:
{
"invoice_type": "SUBSCRIPTION",
"metadata": {
"tenant_id": "12345abcdef67890abcdef12",
"subscription_id": "67890abcdef1234567890123",
"previous_plan": "BASIC",
"new_plan": "PRO"
// NO renewal flag
}
}
Subscription Renewal Invoice:
{
"invoice_type": "SUBSCRIPTION",
"metadata": {
"tenant_id": "12345abcdef67890abcdef12",
"subscription_id": "67890abcdef1234567890123",
"renewal": true, // KEY: Renewal flag present
"billing_cycle": "monthly"
}
}
Appointment Invoice:
{
"invoice_type": "APPOINTMENT",
"metadata": {
"tenant_id": "12345abcdef67890abcdef12",
"appointment_id": "98765fedcba09876543210ab",
"customer_id": "11111aaaaa22222bbbbb33333"
}
}
Webhook Handlers¶
1. Upgrade Handler (handle_invoice_payment_for_subscription)¶
Triggered when: invoice_type == "SUBSCRIPTION" AND no renewal flag
Actions:
- Verify payment status is
paid - Extract
subscription_idfrom metadata - Update subscription:
- Change
plan_typetonew_plan - Keep
current_period_endunchanged - Set
statustoactive - Update invoice status to
paid - Send upgrade confirmation email
- Log subscription change event
2. Renewal Handler (handle_invoice_payment_for_renewal)¶
Triggered when: invoice_type == "SUBSCRIPTION" AND renewal == true
Actions:
- Verify payment status is
paid - Extract
subscription_idandbilling_cyclefrom metadata - Calculate new period end date:
- Monthly: +30 days
- Quarterly: +90 days
- Yearly: +365 days
- Update subscription:
- Keep
plan_typeunchanged - Extend
current_period_endby billing cycle - Update
current_period_startto old end date - Set
next_billing_dateto new end date - Update invoice status to
paid - Send renewal confirmation email
- Log renewal event
3. Appointment Handler (handle_invoice_payment_for_appointment)¶
Triggered when: invoice_type == "APPOINTMENT"
Actions:
- Verify payment status is
paid - Extract
appointment_idfrom metadata - Update appointment status to
confirmed - Update merchant balance (deposit payment - platform fee)
- Send booking confirmation to customer
- Send booking notification to staff
- Log payment event
Webhook Security¶
Important: Paper.id does NOT provide webhook signature verification (no HMAC signing).
Security Measures:
- Idempotency Checks - Track processed invoice IDs to prevent duplicate processing
- Invoice Verification - Validate invoice exists in database before processing
- Amount Verification - Confirm payment amount matches invoice amount
- Tenant Isolation - Ensure invoice belongs to correct tenant
- IP Whitelisting - Only accept webhooks from Paper.id IP ranges (production)
# Idempotency check
processed_invoices = set() # In production: use Redis
async def handle_paper_invoice_webhook(payload: dict):
invoice_id = payload['data']['invoice_id']
# Check if already processed
if invoice_id in processed_invoices:
logger.info(f"Invoice {invoice_id} already processed, skipping")
return {"status": "already_processed"}
# Process webhook
await route_to_handler(payload)
# Mark as processed
processed_invoices.add(invoice_id)
Testing Subscription Flows¶
Prerequisites¶
- ngrok Setup - Expose local server to receive webhooks
-
Paper.id Webhook Configuration
-
Log in to Paper.id dashboard
- Navigate to Settings → Webhooks
- Set webhook URL:
https://your-ngrok-url.ngrok.io/api/v1/webhooks/paper-invoice -
Save configuration
-
Test Tenant - Create tenant with active subscription
Testing Renewal Flow¶
Scenario: Extend PRO Monthly subscription for another month
- API Call:
curl -X POST http://localhost:8000/api/v1/subscriptions/renew \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subscription_id": "507f1f77bcf86cd799439011"
}'
-
Expected Response:
-
Payment Simulation:
- Open
payment_urlin browser - Complete payment using Paper.id test credentials
-
Paper.id sends webhook to your ngrok URL
-
Verify Webhook Routing:
-
Check server logs for:
"Processing renewal webhook for invoice PI-..." -
Verify
renewal: truein metadata -
Verify Subscription Update:
curl -X GET http://localhost:8000/api/v1/subscriptions/current \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Expected: current_period_end extended by 30 days
Testing Upgrade Flow¶
Scenario: Upgrade from FREE to PRO mid-cycle
-
API Call:
-
Expected Response:
-
Payment Simulation:
-
Complete payment via
payment_url -
Paper.id sends webhook (NO renewal flag in metadata)
-
Verify Webhook Routing:
-
Check logs for:
"Processing upgrade webhook for invoice PI-..." -
Verify NO
renewalkey in metadata -
Verify Subscription Update:
curl -X GET http://localhost:8000/api/v1/subscriptions/current \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Expected:
plan_type: "ENTERPRISE"current_period_endUNCHANGED (same expiry date)
Troubleshooting¶
Webhook Not Received¶
Symptoms: Payment completed but subscription not updated
Checks:
- Verify ngrok is running:
ngrok http 8000 - Check Paper.id webhook URL is correct
- Review ngrok web interface:
http://localhost:4040for webhook requests - Check server logs for webhook processing errors
Fix:
- Manually trigger webhook from Paper.id dashboard (Resend Webhook button)
- Or manually update subscription via admin endpoint
Wrong Handler Invoked¶
Symptoms: Renewal processed as upgrade (or vice versa)
Checks:
- Inspect invoice metadata in database:
- Verify
renewalflag is set correctly - Check webhook routing logic
Fix:
- Revert subscription to previous state
- Correct metadata in invoice
- Resend webhook from Paper.id
Prorated Amount Incorrect¶
Symptoms: Upgrade charge doesn't match expected prorated amount
Checks:
- Verify
current_period_enddate is correct - Check days remaining calculation
- Review plan prices in database
Fix:
- Recalculate proration manually
- Issue refund/credit if overcharged
- Update invoice amount
Duplicate Processing¶
Symptoms: Webhook processed multiple times, subscription period extended twice
Checks:
- Verify idempotency checks are working
- Check Redis/cache for processed invoice IDs
- Review webhook retry logs from Paper.id
Fix:
- Revert duplicate changes manually
- Strengthen idempotency logic
- Add database transaction locks
Best Practices¶
For Upgrades¶
✅ DO:
- Always calculate prorated amounts server-side (never trust client)
- Validate new plan is different from current plan
- Set invoice expiration (7 days recommended)
- Send email with payment link
- Wait for webhook confirmation before activating upgrade
❌ DON'T:
- Apply upgrade immediately without payment
- Allow upgrade to same plan (use renewal instead)
- Skip proration calculation
- Trust client-provided amounts
For Renewals¶
✅ DO:
- Charge full period amount (no proration)
- Set
renewal: truein invoice metadata - Extend period by exact billing cycle duration (30/90/365 days)
- Allow renewal even if subscription hasn't expired yet (early renewal)
- Send renewal reminder emails 7 days before expiry
❌ DON'T:
- Prorate renewal charges
- Change plan during renewal
- Allow renewal of canceled subscriptions
- Process renewal without
renewalflag in metadata
For Webhooks¶
✅ DO:
- Implement idempotency checks (prevent duplicate processing)
- Validate invoice amounts match expected values
- Log all webhook payloads for debugging
- Return 200 OK quickly (process asynchronously if needed)
- Use metadata routing flags consistently
❌ DON'T:
- Process webhook without idempotency check
- Trust webhook without verification
- Perform long-running operations in webhook handler
- Fail to log webhook errors
API Reference Summary¶
| Endpoint | Method | Purpose | Key Response |
|---|---|---|---|
/subscriptions/plans |
GET | List all available plans | Plans with pricing and limits |
/subscriptions/current |
GET | Get current subscription | Full subscription details |
/subscriptions/upgrade |
POST | Change plan (prorated) | Invoice with payment URL |
/subscriptions/renew |
POST | Extend period (full charge) | Invoice with payment URL |
/subscriptions/downgrade |
POST | Schedule downgrade | Effective date |
/subscriptions/usage |
GET | Monitor usage vs limits | Usage percentages |
/subscriptions/cancel |
POST | Downgrade to FREE immediately | FREE plan subscription |
/subscriptions/deactivate |
POST | Suspend subscription | Suspended status |
/subscriptions/activate |
POST | Reactivate suspended subscription | Active status |
/subscriptions/payments |
GET | Payment history | List of invoices |
Next Steps:
- Review available plans:
GET /subscriptions/plans - Check current subscription:
GET /subscriptions/current - Test upgrade flow with prorated billing
- Test renewal flow with full period charge
- Monitor usage to optimize plan selection
For webhook integration details, refer to the Webhook Routing Architecture section above.