Hasta Backend
  1. payments
  • lookup
    • Get localised seed data
      GET
    • Get core seed data
      GET
    • Get zodiac signs
      GET
    • Get subscription and pricing plans
      GET
    • Get FAQs
      GET
    • Get notification intervals
      GET
  • audit
    • Get audit logs
      GET
  • auth
    • Register device
      POST
    • Send OTP
      POST
    • Verify OTP and login
      POST
    • Resend OTP
      POST
    • Refresh access token
      POST
    • Logout
      POST
    • Google OAuth login
      POST
  • admin
    • Register admin account
      POST
    • Admin login
      POST
    • Admin logout
      POST
    • Refresh admin access token
      POST
    • Get admin profile
      GET
    • Update admin password
      PUT
    • Edit language
      PUT
    • Add language
      POST
    • Delete language
      DELETE
    • Delete user event logs
      DELETE
    • Clear lookup cache
      DELETE
    • Clear cache
      DELETE
    • List all subscription plans (admin)
      GET
    • Edit a subscription plan (admin)
      PATCH
    • Edit a subscription plan (admin)
      PATCH
    • Sync a recurring plan with Razorpay
      POST
    • Sync a recurring plan with Razorpay
      POST
    • Create a coupon (admin)
      POST
    • List coupons (admin)
      GET
    • Get a coupon (admin)
      GET
    • Get a coupon (admin)
      GET
    • Edit a coupon (admin)
      PATCH
    • Edit a coupon (admin)
      PATCH
    • List redemptions for a coupon (admin)
      GET
    • List redemptions for a coupon (admin)
      GET
    • Create a recurring scheduled notification (admin)
      POST
    • List scheduled notifications (admin)
      GET
    • Pause a scheduled notification (admin)
      PATCH
    • Resume a paused scheduled notification (admin)
      PATCH
  • blogs
    • Admin - get all blogs
    • Admin - delete a blog
    • Get all blogs
    • Get blog categories
    • Admin - get blog categories (name + slug)
    • Admin - edit a blog
    • Update blog status
    • Admin - send blog notification
    • Get blog details
    • Subscribe to blog notifications
    • Unsubscribe from blog notifications
  • feedback
    • Submit a user feedback
    • Get all queries
    • Update user query status
    • Get all reviews
    • Feature or unfeature a review
    • Get all featured reviews
  • events
    • Track a user event
    • Fetch all tracked events
  • notifications
    • Send broadcast notification
    • Send custom notification to a user
    • Send a test notification (deep link redirect check)
    • Fetch user notifications
    • Get unread notification count
    • Mark notification as read
  • reminders
    • Fetch user reminders
    • Create a reminder
    • Update a reminder
    • Delete a reminder
  • user
    • Create / complete user profile
    • Get my profile
    • Get my feature status
    • Update my intents
    • Update profile image
    • Update language preference
    • Update push notification preference
    • Update current location
    • Get my family members
    • Add a family member
    • Delete a family member
    • Update family member profile image
    • Admin - get user details with family
    • Update a family member's intents
    • Admin - list users
    • Admin - get subscribers list
    • Admin - get user details with family
    • Admin - delete user and all family members
    • Admin - update user status
  • palm
    • Get user palm reading
    • Upload user palm image
    • Upload family member palm image
    • Get family member palm reading
  • horoscopes
    • Get user kundali
    • Get family member kundali
    • Get horoscope (daily / weekly / monthly)
  • reports
    • Share a report via email
    • Get shared report PDF link
  • tarot
    • List all tarot cards
    • Get single tarot card by slug
    • Get today's daily tarot card
    • Get a 3-card tarot reading
    • Get tarot reading history
  • marriage
    • Generate marriage compatibility report
    • Get marriage compatibility report
    • List marriage report history
    • Preview the price to upgrade a compatibility report to the full Marriage Report
    • Start the compatibility -> full Marriage Report upgrade purchase
    • Consume the upgrade purchase and regenerate the full Marriage Report
  • child
    • Generate child report (baby names + birth timing)
    • Like or unlike a suggested baby name
    • Get child report (names + timing)
  • dashboard
    • Dashboard overview stats
    • User growth time-series
    • Subscription distribution by plan
    • Top-up & one-time sales distribution
    • Recent activity feed
  • Health
    • Liveness check
  • panchang
    • Get today's panchang for the user's current location
  • festivals
    • List festivals for the user's regions
    • Festival detail with full linked puja vidhis
  • library
    • List library items
    • Library filter options
    • Library content types
    • List my favourite library items
    • Add a library item to favourites
    • Remove a library item from favourites
    • Admin - list library items (incl. drafts)
    • Admin - create a library item
    • Admin - get a library item (incl. drafts)
    • Admin - edit / publish a library item
    • Stream a library item's audio
    • Get a library item
  • mantras
    • List mantras
    • List my favourite mantras
    • Add a mantra to favourites
    • Remove a mantra from favourites
    • Admin - list mantras (incl. drafts)
    • Admin - create a mantra
    • Admin - get a mantra (incl. drafts)
    • Admin - edit / publish a mantra
    • Stream a mantra's chanting audio
    • Get a mantra
  • nandini
    • Send a message and/or image(s)/PDF(s) to Nandini (reply is streamed over WebSocket)
    • List my Nandini conversations
    • Get a conversation's messages
    • Delete a conversation
  • payments
    • Validate a coupon code
      POST
    • Start a plan/report/topup purchase
      POST
    • Verify a completed Razorpay payment
      POST
    • Cancel a recurring subscription
      POST
    • Pause a recurring subscription
      POST
    • Resume a paused subscription
      POST
  • subscriptions
    • List my purchase history
    • Get one purchase's status
  • public
    • Get today's panchang for any location - no auth required
    • Free kundali preview - no auth, no account required
    • Search a place by name (keystroke-aware) - no auth required
    • Resolve a place_id to full location + timezone - no auth required
  • coupons
    • List currently applicable coupons
    • Get a currently applicable coupon
  • account-deletion
    • Request account & data deletion
  1. payments

Start a plan/report/topup purchase

POST
https://uat-api.hastaai.in/api/v1/subscriptions/checkout
payments
Last modified:2026-08-20 11:43:27
Request: application/json
Starts a purchase for any plan slug (Sage/Guru tier, topup, or one-time report). Recurring tiers create a real Razorpay Subscription (auto-charging mandate); one-time products create a Razorpay Order. An explicit coupon_code is validated and applied; if omitted, a prior sticky coupon on the same plan is auto-reapplied (locked-in renewal pricing). requires_payment=false means the purchase was free and is already active - no Razorpay step needed.
Idempotent - safe to call again: if the same user/plan/coupon combo already has a still-fresh pending purchase (within PENDING_PURCHASE_TTL_MINUTES, default 60), it's reconciled against Razorpay live before deciding what to do - never blindly reopened for a fresh payment attempt. Three outcomes: (1) Razorpay confirms it was already paid -> activated on the spot, response has requires_payment: false, status: "active" (skip straight to step 2, no purchase needed); (2) Razorpay shows a live but not-yet-captured attempt (authorized moments ago, webhook hasn't landed) -> requires_payment: false, status: "processing", reason: "<user-facing message>" - show that message and do NOT reopen the payment widget, the user already has money on hold; (3) genuinely abandoned (dismissed the widget, never attempted payment) -> the SAME order/subscription is returned for a fresh attempt, exactly as before. This means a double-tapped 'Buy' never creates duplicate Razorpay orders AND never shows 'purchase again' to someone who already paid. Once the TTL window passes, the abandoned row is swept to failed by a daily cron and the next call mints a fresh one.
Also idempotent for a one-time report you already hold an open credit for (2026-08-20): for palm/tarot/marriage/child/compatibility specifically (never topups, those are legitimately re-buyable), if you already have a PAID, active credit for that exact report that hasn't been delivered yet - either because you never called the generate endpoint, OR you did and it failed/was rejected (e.g. palm image validation) - this call returns requires_payment: false, status: "active" with that SAME purchase_key instead of opening Razorpay again. In other words: it is always safe for the frontend to call this endpoint again on any retry flow, including after a report-generation failure - you never need to special-case "did they already pay for this" client-side, the backend always knows. A credit only stops being reusable once its report actually reaches done.
If the Razorpay widget fails or the user dismisses it: no backend call is needed - just call this endpoint again to retry. The failed/abandoned purchase's status update happens asynchronously via Razorpay's webhook, independent of the frontend.
Razorpay Checkout.js integration - checkout_type changes which fields you use: read checkout_type ("order" vs "subscription") off the response and branch - the two flows do NOT share field names, mixing them up breaks the widget/payment callback (seen live 2026-08-20 on Guru yearly: widget opened, payment went through, then a client-side "payment id missing" error because the success handler assumed an order-shaped response).
checkout_type: "order" (topup, one-time report): pass { key: razorpay_key_id, order_id: razorpay_order_id, amount, currency } into Checkout.js. On success the handler receives { razorpay_payment_id, razorpay_order_id, razorpay_signature } - send all three, as razorpay_order_id, to /payments/verify.
checkout_type: "subscription" (Sage/Guru recurring): pass { key: razorpay_key_id, subscription_id: razorpay_subscription_id } into Checkout.js - do NOT pass order_id/amount/currency, there is no razorpay_order_id in this response at all. On success the handler receives { razorpay_payment_id, razorpay_subscription_id, razorpay_signature } - note there is no order_id here either - send those three, as razorpay_subscription_id, to /payments/verify.
This endpoint never generates a report - it only creates/activates a purchase credit. For a one-time report (palm/tarot/marriage/child/compatibility), calling this endpoint is ALWAYS step 1 of 2, whether the purchase is free or paid: after checkout succeeds (requires_payment=false) or after /payments/verify confirms payment (requires_payment=true), the frontend must separately call that report's own generate endpoint (e.g. POST /palm/user, POST /marriage/report) as step 2 - THAT call is what finds the now-active credit and actually starts generation. This is true even for a 100%-off coupon: it skips the Razorpay widget in the middle, but step 2 still has to be called explicitly - nothing auto-generates on checkout alone.
Message balance bonus: any plan whose features.total_message/features.messages is > 0 (topups, and one-time reports like tarot/palm/marriage/child) seeds a messages_remaining balance on this purchase at checkout time - paid or free, doesn't matter. Once the user's daily plan quota (messages_per_day) runs out on any day, Nandini chat draws 1 from the OLDEST purchase with a remaining balance (FIFO across topups and report purchases alike) before blocking - this balance never resets and carries forward indefinitely until fully drawn down.
Body: subscription_slug, coupon_code (optional)
Success: 200 - checkout details for the Razorpay Checkout widget, or an already-active purchase. valid: false with a reason ("Invalid subscription plan" for a bad subscription_slug, or "This plan is not currently available" if it's been deactivated) means nothing was created - requires_payment and purchase_key are then meaningless/absent. This mirrors /coupons/validate: a bad subscription_slug is a client input mistake, never a 404.
Errors: 404 - authenticated user record not found (should not normally happen) | 422 - coupon invalid, or plan not yet synced with Razorpay

Request

Authorization
Bearer Token
Provide your bearer token in the
Authorization
header when making requests to protected resources.
Example:
Authorization: Bearer ********************
or
Body Params application/jsonRequired

Example
{
    "coupon_code": "FIRSTPLAN20",
    "subscription_slug": "sage-monthly"
}

Request Code Samples

Shell
JavaScript
Java
Swift
Go
PHP
Python
HTTP
C
C#
Objective-C
Ruby
OCaml
Dart
R
Request Request Example
Shell
JavaScript
Java
Swift
curl --location 'https://uat-api.hastaai.in/api/v1/subscriptions/checkout' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
    "coupon_code": "FIRSTPLAN20",
    "subscription_slug": "sage-monthly"
}'

Responses

🟢200OK
application/json
Successful Response
Bodyapplication/json

Example
{
    "success": true,
    "message": "Success",
    "data": {
        "valid": true,
        "requires_payment": true,
        "checkout_type": "subscription",
        "purchase_key": "Purchase-83921",
        "razorpay_subscription_id": "sub_00000000000001",
        "short_url": "https://rzp.io/i/abc123",
        "razorpay_key_id": "rzp_test_TEbtpAni331ygI",
        "amount": 239.2,
        "currency": "INR"
    }
}
🟠401Unauthorized
🟠404Record Not Found
🟠406
🟠422Parameter Error
🟠429
Modified at 2026-08-20 11:43:27
Previous
Validate a coupon code
Next
Verify a completed Razorpay payment
Built with