Subscription and Credits
This guide explains the Face2Social subscription model, credit system, and how payment gating works in the search flow.
Subscription plans
Retrieve the list of available plans with GET /subscription/plans. This endpoint is public (no token required):
curl https://dashboard.face2social.com/api/v1/subscription/plans
The response is an array of SubscriptionPlan objects. Each plan has id, title, price, interval and credits, plus optional display fields (description, features, badge, discount, savings).
Start a subscription
curl -X POST https://dashboard.face2social.com/api/v1/subscription/start/{planId} \
-H "Authorization: Bearer <your-token>"
The {planId} path parameter is taken from the plans list. The response contains checkout_url — the Stripe checkout page the user opens to pay. An optional returnTo query parameter accepts a relative path (e.g. search/abc123/matches) to redirect the user back after successful payment.
Check current subscription
curl https://dashboard.face2social.com/api/v1/subscription/info \
-H "Authorization: Bearer <your-token>"
Returns {subscription, portalUrl} with the active subscription details. Credit balance is available separately via GET /credits.
Cancel
curl -X POST https://dashboard.face2social.com/api/v1/subscription/cancel \
-H "Authorization: Bearer <your-token>"
Manage (Stripe billing portal)
curl https://dashboard.face2social.com/api/v1/subscription/manage \
-H "Authorization: Bearer <your-token>"
Returns a Stripe billing portal URL where the user can manage payment methods, invoices, and billing details.
Sync after checkout
If the user completes a Stripe checkout session externally (e.g. via an email link), sync the subscription state by passing the csi query parameter to:
GET /subscription/sync-by-session?csi=<id>
Credits
Credits are the currency for search results. Each successful search unlock costs one credit.
Check your credit balance
curl https://dashboard.face2social.com/api/v1/credits \
-H "Authorization: Bearer <your-token>"
Returns a CreditsInfo object:
| Field | Meaning |
|---|---|
available | Credits the user can spend now |
total | plan + referral credits |
plan | Credits from the subscription plan |
referral | Referral credits — spent first, carried over to the next month |
used | Credits used this month |
searchesUsed / searchesLimit | Free-search usage for users without a subscription |
Search limit for users without a subscription
Starting a search (POST /search/upload) never spends a credit, but a user without an active subscription can start only a limited number of searches. After that, upload returns 402 Payment Required ("search limit reached") and GET /users/me reports canSearch: false until the user buys a plan.
How credits are charged: payment_stage
The server-side configuration key common.payment_stage controls where in the search flow a credit is deducted. Clients read the active mode from the paymentStage field of GET /users/me (missing or null means selection). There are two modes:
payment_stage=selection
A credit is deducted when the user calls POST /search/{searchId}/select to confirm the matches. If credits.Available == 0, the API returns 402 Payment Required at this step and the results remain locked.
payment_stage=results
The credit is not deducted at selection. Instead it is deducted (or the results are blurred) when the user calls GET /search/{searchId}/results. This call never returns 402: without a credit it returns 200 with isBlurred: true and masked profile data.
The paywall decision at results-time uses two signals fetched in parallel: credits.Available and results_credit_deducted (a per-search flag set once the credit has been charged):
credits.Available | results_credit_deducted | Outcome |
|---|---|---|
0 | false | Blurred — paywall: no credit, results never unlocked |
0 | true | Unblurred — already paid; revisiting stays unlocked |
> 0 | false | Deduct now, return unblurred results |
> 0 | true | Unblurred — idempotent (no double charge) |
With payment_stage=results and credits.Available == 0, always check results_credit_deducted before blurring — otherwise a results view that the user already paid for gets re-blurred on revisit.
Offers
The API exposes an offer system for promotional or upsell pricing:
GET /offer— returns the current offer assigned to the authenticated user (e.g. a discounted subscription plan).GET /offer/{hash}— resolves an email deep-link to start checkout for an assigned offer.
Referrals
The API includes a referral program:
POST /ref/{code}— saves a referral or partner-link code to the session (call before sign-up to attribute the new user). An unknown partner code returns404.GET /referral/info— returns referral information for the authenticated user.POST /referral/acknowledge— acknowledges that the user has viewed a referral hint.
Error codes
| HTTP status | Meaning |
|---|---|
402 Payment Required | On POST /search/upload: the search limit for users without a subscription is reached. On POST /search/{searchId}/select (only with payment_stage=selection): no credits available; the selection is rolled back, so the call can be repeated after buying credits. GET /search/{searchId}/results never returns 402; it blurs instead |
400 Bad Request | On select: matches already selected, or more than maxSelectable IDs. On results: matches not selected yet |
401 Unauthorized | Token missing, expired, or invalid |
404 Not Found | Plan or subscription not found |