Data Types & Enums
This page documents the data types and enumerations used by the Joy Subscription Storefront REST API (v1). Every value listed here is part of the stable API contract — integrate against these machine-readable values rather than display strings.
All timestamps returned by the API are formatted as ISO 8601 strings (for example 2026-06-25T09:30:00.000Z). Where a timestamp can be absent, the field is typed as string | null.
Subscription contract status
The contract status field is returned in uppercase. It describes the current lifecycle state of a subscription contract.
| Value | Meaning |
|---|---|
ACTIVE | Active, billing on schedule. |
PAUSED | Paused by customer or merchant. |
CANCELLED | Cancelled. |
EXPIRED | Reached its end / max cycles. |
FAILED | Billing failure state. |
PENDING | Not yet active. |
DECLINED | Declined. |
Status change actions (request input)
PUT /subscriptions/{contractId}/status accepts a lowercase status action in the request body. Note that these input actions are distinct from the uppercase contract status values returned in responses.
| Action | Effect |
|---|---|
pause | Pause the contract. |
resume | Resume a paused contract. |
cancel | Cancel the contract. |
Example request body:
{ "status": "pause" }Delivery / billing interval
frequency updates use an uppercase interval enum plus a positive integer count. This controls how often the subscription is delivered and billed.
interval | intervalCount |
|---|---|
DAY / WEEK / MONTH / YEAR | positive integer |
The frequency payload is nested under an input object. Example for "every 2 months":
{ "input": { "interval": "MONTH", "intervalCount": 2 } }Billing-cycle action types
PUT /subscriptions/{contractId}/billing-cycle accepts a type describing the action to perform on a billing cycle.
type | Meaning |
|---|---|
skip | Skip a cycle. |
reschedule | Move a cycle's billing date (requires newBillingDate). |
resume | Un-skip a cycle. |
order_now | Bill the cycle immediately. |
When type is reschedule, newBillingDate (ISO 8601) is required. An unrecognized type is rejected with 400 INVALID_PARAM.
{ "type": "reschedule", "cycleIndex": 4, "newBillingDate": "2026-07-15T00:00:00.000Z" }Discount type
PUT /subscriptions/{contractId}/subscription-discount accepts an uppercase discountType.
| Value | Meaning |
|---|---|
PERCENTAGE | Percentage-based discount. |
FIXED_AMOUNT | Fixed monetary amount discount. |
This action is merchant-gated: it returns 403 FORBIDDEN unless the merchant has enabled customerPortal.allowSubscriptionDiscount.
Cancellation reason codes
POST /subscriptions/{contractId}/cancel accepts a reasonCode. The reason code is threaded through to the cancel webhook payload.
| Value |
|---|
too_expensive |
no_longer_needed |
switching |
quality_issue |
other |
Example cancel request body:
{ "reasonCode": "too_expensive", "comment": "Found a cheaper plan" }Trial status (response projection)
GET /subscriptions/{contractId}/trial returns the following projection. The endpoint validates the path parameter inline and returns 422 if the contract id is malformed.
| Field | Type | Notes |
|---|---|---|
contractId | number | |
hasTrial | boolean | Whether the contract has a trial configured. |
isInTrial | boolean | Whether the contract is currently within its trial window. |
trialEndsAt | string | null | ISO 8601 timestamp when the trial ends. |
daysRemaining | number | null | Days left in the trial. |
trialProcessed | boolean | Whether the trial-end action has already been processed. |
afterAction | pause | continue | null | What happens when the trial ends. |
status | string | null | Contract status. |
Example response:
{
"success": true,
"data": {
"contractId": 123456789,
"hasTrial": true,
"isInTrial": true,
"trialEndsAt": "2026-07-10T00:00:00.000Z",
"daysRemaining": 15,
"trialProcessed": false,
"afterAction": "continue",
"status": "ACTIVE"
}
}ISO 8601 timestamps
All date-time values exchanged with the Storefront API use the ISO 8601 format with a UTC offset, for example:
2026-06-25T09:30:00.000ZThis applies to both response fields (for example trialEndsAt) and request fields (for example newBillingDate on the billing-cycle endpoint and the optional date query parameter on GET /subscriptions/{contractId}/next-charge).
Every API response includes a top-level timestamp field — an ISO 8601 UTC string stamped automatically by middleware on any response body that carries a success field. Example:
{
"success": true,
"data": { },
"timestamp": "2026-06-25T09:30:00.000Z"
}