API reference Customer Subscriptions
Customer Subscriptions API Endpoints
Recurring charges a company bills one of its own customers automatically on a configurable schedule (every N weeks / months / years), against a payment method saved in the customer's vault (see payments-endpoints.md, "Save Customer Payment Method"). Not to be confused with the company's own SaaS subscription (saas-endpoints.md).
Authentication
All endpoints require a Bearer token (Authorization: Bearer <token>). Reading is open to every employee; creating, updating, deleting and charging require employeeType ADMIN or LEAD_TECHNICIAN (403 otherwise).
Base Path
- Production:
https://api.tradesmentechnologies.com/prod/customer-subscriptions
Concepts
Subscription record shape
{
"companyId": "string",
"subscriptionId": "uuid",
"customerId": "string",
"name": "Monthly lawn care",
"description": "Mow, edge and blow every visit",
"amount": 12500,
"intervalUnit": "MONTH",
"intervalCount": 1,
"nextChargeDate": "2026-09-15",
"anchorDay": 15,
"endDate": null,
"vaultCustomerId": "string",
"paymentMethodId": "string",
"paymentMethodType": "card",
"paymentMethodLast4": "4242",
"paymentMethodBrand": "visa",
"sendReceipts": true,
"status": "ACTIVE",
"failedAttempts": 0,
"chargeCount": 3,
"totalCharged": 37500,
"lastChargeAt": "2026-08-15T15:00:04.000Z",
"lastChargeStatus": "APPROVED",
"lastAttemptDate": "2026-08-15",
"charges": [
{
"paymentId": "TXN#uuid",
"transactionId": "string",
"amount": 12500,
"status": "APPROVED",
"responseText": "Approved",
"chargedAt": "2026-08-15T15:00:04.000Z",
"manual": false
}
],
"createdByEmployeeId": "string",
"createdAt": "2026-06-15T18:22:10.000Z",
"updatedAt": "2026-08-15T15:00:04.000Z"
}
| Field | Notes |
|---|---|
name / description |
Trimmed. name is required, at most 200 characters; description is optional, at most 2000 characters (blank or null stores null). |
amount |
Integer cents, positive. Charged as-is every cycle. |
intervalUnit / intervalCount |
Schedule: every intervalCount (1–52) WEEK / MONTH / YEAR. |
nextChargeDate |
Date-only YYYY-MM-DD. The next charge happens in the daily run on or after this date (today counts). On create this is the first charge date. |
anchorDay |
Server-managed: day-of-month of the date the schedule was (re)anchored on. Monthly/yearly steps clamp to the last day of shorter months without drifting (Jan 31 → Feb 28 → Mar 31). Re-derived whenever nextChargeDate is set by a client. |
endDate |
Optional date-only. When advancing the schedule after a charge would land past it, the subscription becomes COMPLETED. Must be on or after nextChargeDate. |
vaultCustomerId, paymentMethodLast4, paymentMethodBrand |
Server-managed snapshot taken from the vault when the payment method is set. |
paymentMethodId / paymentMethodType |
Must reference an existing method in the customer's vault (card or ach). |
sendReceipts |
Stored, default true — but receipt emails are currently disabled platform-wide, so no email is sent on an approved charge. When enabled: the customer is emailed a receipt after every approved charge (needs an email on file; platform-suppressed addresses are skipped). |
failedAttempts |
Consecutive declines. Reset on approval or when the client sets status: ACTIVE. |
charges |
Most recent first, capped at 24. Every attempt is also a row in the payments table (GET /payments/transactions — documentId is null, customerSubscriptionId set). |
lastAttemptDate |
Server-managed once-per-day guard. |
Statuses
| Status | Meaning |
|---|---|
ACTIVE |
Charged by the daily run when nextChargeDate arrives. |
PAUSED |
Skipped by the daily run; the schedule does not advance. Resume with status: ACTIVE. |
PAYMENT_FAILED |
Three consecutive declines. Stops being picked up. Fix the payment method (PUT) and/or set status: ACTIVE, or use Charge Now (an approved manual charge reactivates it). |
CANCELLED |
Client-set terminal state. Cannot be reactivated. |
COMPLETED |
Server-set terminal state when the schedule passes endDate. Cannot be reactivated. |
Clients may set ACTIVE, PAUSED, CANCELLED only.
Daily run
A scheduled process runs daily at 15:00 UTC. For every ACTIVE subscription with nextChargeDate <= today that has not been attempted today it charges the vaulted method through the company's own TalusPay merchant (ACH uses the merchant's ACH processor), writes a payments-table row, mirrors approved charges to QuickBooks when connected, and:
- Approved:
nextChargeDateadvances one interval (or the subscription becomesCOMPLETEDpastendDate) and counters update. No email is sent on approval for now (receipts are built but switched off — seesendReceipts). - Declined:
failedAttemptsincrements and the charge is retried on the next daily run; after the third consecutive decline the status becomesPAYMENT_FAILED. The company's email on file receives a notice after every decline.
Session bootstrap: POST /accounts/login (and the MFA/platform-admin variants) returns every subscription of the company as the top-level customerSubscriptions list.
Endpoints
Create Subscription
- Path:
/customer-subscriptions - Method: POST
- Roles: ADMIN, LEAD_TECHNICIAN
Request Body
{
"customerId": "string",
"name": "Monthly lawn care",
"description": "Mow, edge and blow every visit",
"amount": 12500,
"intervalUnit": "MONTH",
"intervalCount": 1,
"nextChargeDate": "2026-09-15",
"endDate": null,
"paymentMethodId": "string",
"paymentMethodType": "card",
"sendReceipts": true,
"status": "ACTIVE"
}
Required: customerId, name, amount, intervalUnit, intervalCount, nextChargeDate, paymentMethodId, paymentMethodType. Optional: description, endDate, sendReceipts (default true), status (ACTIVE default, or PAUSED).
Response
- Success:
201 Created—{ "subscription": { ...record } } - Error:
400 Bad Request(validation; company not set up for payments; client has no saved payment methods; payment method not in the vault;endDatebeforenextChargeDate;status: CANCELLED),401,403,404 Not Found(customer),500
List Subscriptions
- Path:
/customer-subscriptions - Method: GET
- Query:
customerId(optional) — only that customer's subscriptions
Response
- Success:
200 OK—{ "subscriptions": [ ...records ] } - Error:
401,500
Get Subscription
- Path:
/customer-subscriptions/{subscriptionId} - Method: GET
Response
- Success:
200 OK—{ "subscription": { ...record } } - Error:
401,404 Not Found,500
Update Subscription
- Path:
/customer-subscriptions/{subscriptionId} - Method: PUT
- Roles: ADMIN, LEAD_TECHNICIAN
Partial update — only the keys present change. Accepts the same fields as create except customerId (immutable, ignored). Notes:
nextChargeDatere-anchors the schedule on that day-of-month.paymentMethodId/paymentMethodTypeare re-validated against the vault and the brand/last-4 snapshot refreshed.status: ACTIVEresetsfailedAttempts(resume fromPAUSED/ recover fromPAYMENT_FAILED).CANCELLED/COMPLETEDsubscriptions cannot be reactivated or paused (400).description: null/endDate: nullclear the field.
Request Body
{
"amount": 13500,
"status": "PAUSED"
}
Response
- Success:
200 OK—{ "subscription": { ...updated record } } - Error:
400,401,403,404,500
Delete Subscription
- Path:
/customer-subscriptions/{subscriptionId} - Method: DELETE
- Roles: ADMIN, LEAD_TECHNICIAN
Removes the row (the schedule and its charges history). Past charges remain in the payments table. Prefer status: CANCELLED when the history should stay visible.
Response
- Success:
200 OK—{ "message": "Subscription deleted successfully" } - Error:
400,401,403,404,500
Charge Now
- Path:
/customer-subscriptions/{subscriptionId}/charge - Method: POST
- Roles: ADMIN, LEAD_TECHNICIAN
Charges the subscription immediately, out of schedule, using the same routine as the daily run (a payments row, receipt, counters, and — on approval — the schedule advancing from the stored nextChargeDate). Allowed on ACTIVE, PAUSED and PAYMENT_FAILED subscriptions (an approved charge reactivates a PAYMENT_FAILED one; a PAUSED one stays paused). Not allowed on CANCELLED / COMPLETED.
Response
- Success:
200 OK
A decline is also{ "approved": true, "paymentId": "TXN#uuid", "transactionId": "string", "subscription": { } }200withapproved: false,paymentIdandresponseText(the processor's reason, ornull); thesubscriptionreflects the updated counters/status. - Error:
400 Bad Request(missingsubscriptionId; terminal status; company not set up for payments),401,403,404,409 Conflict(a concurrent charge claimed the day — body{ "message": "Subscription was not charged", "reason": "ALREADY_ATTEMPTED_TODAY" }),500
Enums
CustomerSubscriptionStatus
ACTIVE, PAUSED, PAYMENT_FAILED, CANCELLED, COMPLETED
CustomerSubscriptionIntervalUnit
WEEK, MONTH, YEAR