Subscriptions API
GET /subscriptions/status
Get the current user's subscription status, limits, and usage.
Auth required: Yes
Response (200):
{
"tier": "pro",
"expires_at": "2027-05-26T00:00:00Z",
"grandfathered": false,
"limits": {
"ai_tasks_per_month": 200,
"voice_enabled": true
},
"usage": {
"ai_tasks_used": 15,
"ai_tasks_remaining": 185,
"reset_at": "2026-05-01T00:00:00Z"
}
}Limits by tier:
| Tier | ai_tasks_per_month | voice_enabled | family_enabled |
|---|---|---|---|
| free | 5 | false | -- |
| pro | 200 | true | -- |
| family | 200 | true | true |
curl http://localhost:3000/api/v1/subscriptions/status \
-H "Authorization: Bearer <token>"POST /subscriptions/sync
Trigger an async subscription sync from RevenueCat. Call this after a purchase or restore in the mobile app.
Auth required: Yes
Response (200):
{
"message": "Subscription sync queued",
"current_tier": "pro"
}The sync happens asynchronously via SyncSubscriptionJob. The user's tier will update once the job completes.
curl -X POST http://localhost:3000/api/v1/subscriptions/sync \
-H "Authorization: Bearer <token>"POST /webhooks/revenuecat
Receive subscription lifecycle events from RevenueCat (renewals, cancellations, billing issues).
Auth required: No (webhook signature verified via REVENUECAT_WEBHOOK_SECRET)
Request body:
RevenueCat sends its standard webhook payload. The backend only reads event.type and event.app_user_id.
Response: 200 OK (always, to acknowledge receipt)
Verification:
The webhook verifies the Authorization: Bearer <secret> header against the REVENUECAT_WEBHOOK_SECRET environment variable. If the secret is not configured, verification is skipped.
WARNING
Configure REVENUECAT_WEBHOOK_SECRET in production. Without it, any request to this endpoint will be accepted.
curl -X POST http://localhost:3000/api/v1/webhooks/revenuecat \
-H "Authorization: Bearer <webhook_secret>" \
-H "Content-Type: application/json" \
-d '{"event":{"type":"INITIAL_PURCHASE","app_user_id":"1"}}'