The Subscriptions module helps you stay on top of every recurring charge — streaming services, cloud tools, gym memberships, and anything else that hits your account on a regular cycle. You create a subscription once with its billing cycle and linked account/category, then Finper automatically calculates the next expected payment date from your real transaction history. A built-in detection engine watches every new transaction you record and surfaces likely subscription matches for you to confirm or dismiss in one click.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/soker90/finper/llms.txt
Use this file to discover all available pages before exploring further.
Data Model
Subscription Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | ✅ | Service name, e.g. "Netflix" |
amount | number | ✅ | Expected charge amount per cycle |
currency | string | ❌ | ISO 4217 currency code (e.g. "EUR", "USD") |
cycle | number | ✅ | Billing frequency in months — an integer from 1 to 60 (e.g. 1 = monthly, 3 = quarterly, 12 = annually) |
nextPaymentDate | number | null | calculated | Unix timestamp (ms) of the next expected payment; null when no transactions are linked yet — never set manually |
categoryId | ObjectId | ✅ | Expense category for matching transactions |
accountId | ObjectId | ✅ | Account that receives the charge |
logoUrl | string | ❌ | URL of the service’s logo image |
cycle is a number, not a named string. Pass the billing interval in months: 1 for monthly, 2 for bimonthly, 3 for quarterly, 6 for semi-annually, 12 for annually, or any integer from 1 to 60 for custom intervals.SubscriptionCandidate Fields
ASubscriptionCandidate is a temporary record created automatically when Finper detects that a new transaction might be a subscription payment. It sits in a pending queue until you confirm or discard it.
| Field | Type | Description |
|---|---|---|
transactionId | ObjectId | The transaction detected as a possible payment |
subscriptionIds | ObjectId[] | One or more subscriptions whose nextPaymentDate matches the transaction date |
createdAt | Date | Auto-set creation timestamp (newest first in listing) |
Endpoints
| Method | Path | Description |
|---|---|---|
POST | /api/subscriptions | Create a subscription |
GET | /api/subscriptions | List all subscriptions, ordered by nextPaymentDate ascending |
PUT | /api/subscriptions/:id | Edit a subscription |
DELETE | /api/subscriptions/:id | Delete a subscription |
GET | /api/subscriptions/:id/transactions | List transactions already linked to this subscription |
GET | /api/subscriptions/:id/matching-transactions | List unlinked transactions that could belong to this subscription (max 50) |
POST | /api/subscriptions/:id/link-transactions | Link a batch of transactions to the subscription |
DELETE | /api/subscriptions/:id/unlink-transactions/:transactionId | Remove the link between a transaction and this subscription |
GET | /api/subscriptions/candidates | List pending candidates awaiting your confirmation |
POST | /api/subscriptions/candidates/:id/assign | Confirm a candidate — links the transaction to the chosen subscription |
POST | /api/subscriptions/candidates/:id/dismiss | Discard a candidate without linking anything |
Next Payment Date Calculation
nextPaymentDate is always computed — Finper never stores a date you supply manually.
The calculation rule is straightforward:
advanceDate advances a timestamp by exactly cycle months using JavaScript’s native Date.setMonth(), so edge cases (e.g. month-end overflow) follow JavaScript’s built-in date arithmetic.
This recalculation runs automatically after every link or unlink operation, so nextPaymentDate is always consistent with the latest payment you have recorded.
Until at least one transaction is linked,
nextPaymentDate is null. The subscription still appears in the list — it is sorted after all entries with a concrete date.Automatic Candidate Detection
When you create any transaction, Finper runs a background check to see if it could be a subscription payment. This check is fire-and-forget — it runs asynchronously and never delays or fails the transaction creation call.Transaction is saved
Your
POST /api/transactions call completes normally and returns the new transaction.±7-day window check
Finper searches for subscriptions that belong to the same user, use the same
accountId and categoryId as the new transaction, and whose nextPaymentDate falls within ±7 days of the transaction’s date.Candidate record created
If one or more matching subscriptions are found, a single
SubscriptionCandidate is created linking the transaction to all matching subscriptions. If no matches are found, nothing is created.Review in the candidates queue
Call
GET /api/subscriptions/candidates to see all pending suggestions. Each candidate shows the transaction details alongside the possible subscription matches.The ±7-day window intentionally tolerates real-world billing variance — charges that arrive a few days early or late are still detected as likely matches.
Linking Transactions Manually
If Finper’s auto-detection hasn’t surfaced a particular transaction as a candidate (perhaps the subscription was created after the transactions were recorded), you can link them manually:Discover matching transactions
GET /api/subscriptions/:id/matching-transactions returns up to 50 unlinked transactions that share the subscription’s categoryId and accountId, sorted newest first.Common Cycle Values
Thecycle field accepts any integer from 1 to 60. Common values:
cycle value | Billing Frequency |
|---|---|
1 | Monthly |
2 | Every 2 months |
3 | Quarterly |
6 | Semi-annually |
12 | Annually |
Examples
Creating a Subscription
nextPaymentDate: null because no transactions are linked yet.
Listing and Assigning Candidates
nextPaymentDate on the linked subscription is recalculated automatically and the candidate record is removed from the queue.