Skip to content
SoundSyncAudio software platform

Guide

Subscription Bundle API

Versioned bundle definitions, signed-in checkout, customer plan changes, billing setup, and recurring reporting.

SoundSync

Customer checkout and billing

Current route and service contracts. Examples below are illustrative; live provider outcomes and client-host testing are separate checks.

GET /api/subscriptions/bundles/:bundleId

Read a published bundle and its current version.

Auth
Public request; operation-specific verification still applies
Scope
Published and available bundle data only; no private customer or subscription records.
Request fields
  • bundleId path
Returns
{ bundle } with interval, amounts, trial/grace settings, membershipMode, fulfillmentMode, versionId, and products.
Side effects
Retrieves the authorized resource. Operation-specific notes identify preparation or referral recording where applicable.
Notes
Public read; unpublished or unavailable bundles cannot be used for checkout.
Request, response and error reference

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "bundle": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "status": {
          "type": "string"
        },
        "fulfillmentMode": {
          "enum": [
            "access",
            "keep_products"
          ]
        },
        "membershipMode": {
          "enum": [
            "selected_products",
            "all_products"
          ]
        },
        "billingInterval": {
          "type": "string"
        },
        "recurringAmountCents": {
          "type": "integer",
          "minimum": 0
        },
        "products": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "productId": {
                "type": "integer",
                "minimum": 1
              },
              "name": {
                "type": "string"
              }
            },
            "additionalProperties": true
          }
        }
      },
      "additionalProperties": true
    }
  },
  "required": [
    "bundle"
  ],
  "additionalProperties": true
}
POST /api/subscriptions/bundles/checkout

Create a Stripe checkout session for the signed-in buyer.

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • header Idempotency-Key: string. Reuse for the same logical checkout; do not change the request under the same key.
  • bundleId: integer (required)
  • promoCode: string
  • taxQuoteId: string
  • idempotencyKey: string
  • marketingOptOut: boolean
  • marketingOptIn: boolean
Returns
201: provider, checkoutSessionId, url, customerId, bundle, and conflicts.
Side effects
Create a Stripe checkout session for the signed-in buyer. Review the confirmation and retry requirements before repeating the request.
Notes
The server supplies buyer identity and required environment. Keep-products ownership requires eligible live payment evidence; a session URL does not grant ownership.
Request, response and error reference

Query and header parameters

[
  {
    "name": "Idempotency-Key",
    "in": "header",
    "required": false,
    "schema": {
      "type": "string"
    },
    "description": "Reuse for the same logical checkout; do not change the request under the same key."
  }
]

Request (application/json)

{
  "type": "object",
  "properties": {
    "bundleId": {
      "type": "integer",
      "minimum": 1
    },
    "promoCode": {
      "type": "string"
    },
    "taxQuoteId": {
      "type": "string"
    },
    "idempotencyKey": {
      "type": "string"
    },
    "marketingOptOut": {
      "type": "boolean"
    },
    "marketingOptIn": {
      "type": "boolean"
    }
  },
  "required": [
    "bundleId"
  ],
  "additionalProperties": true
}

HTTP 201 response (application/json)

{
  "type": "object",
  "properties": {
    "provider": {
      "const": "stripe"
    },
    "checkoutSessionId": {
      "type": "string"
    },
    "url": {
      "type": "string",
      "format": "uri"
    },
    "customerId": {
      "type": "integer",
      "minimum": 1
    },
    "bundle": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "status": {
          "type": "string"
        },
        "fulfillmentMode": {
          "enum": [
            "access",
            "keep_products"
          ]
        },
        "membershipMode": {
          "enum": [
            "selected_products",
            "all_products"
          ]
        },
        "billingInterval": {
          "type": "string"
        },
        "recurringAmountCents": {
          "type": "integer",
          "minimum": 0
        },
        "products": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "productId": {
                "type": "integer",
                "minimum": 1
              },
              "name": {
                "type": "string"
              }
            },
            "additionalProperties": true
          }
        }
      },
      "additionalProperties": true
    },
    "conflicts": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {},
        "additionalProperties": true
      }
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

POST /api/subscriptions/bundles/change-preview

Inspect a proposed plan change and its timing.

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • currentSubscriptionId: integer (required)
  • targetBundleId: integer (required)
  • effectiveMode: immediate | next_renewal
Returns
201: preview, proposed timing, amounts, and conflict context.
Side effects
Inspect a proposed plan change and its timing. Review the confirmation and retry requirements before repeating the request.
Notes
The current subscription must belong to the signed-in buyer. Read the preview before confirmation.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "currentSubscriptionId": {
      "type": "integer",
      "minimum": 1
    },
    "targetBundleId": {
      "type": "integer",
      "minimum": 1
    },
    "effectiveMode": {
      "enum": [
        "immediate",
        "next_renewal"
      ]
    }
  },
  "required": [
    "currentSubscriptionId",
    "targetBundleId"
  ],
  "additionalProperties": true
}

HTTP 201 response (application/json)

{
  "type": "object",
  "properties": {
    "preview": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "format": "uuid"
        },
        "effectiveMode": {
          "type": "string"
        }
      },
      "additionalProperties": true
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

POST /api/subscriptions/bundles/change-confirm

Confirm a still-valid plan preview.

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • previewId: string (required)
  • idempotencyKey: string (required)
Returns
201 for a new confirmation or 200 for a duplicate.
Side effects
Confirm a still-valid plan preview. Review the confirmation and retry requirements before repeating the request.
Notes
Do not reuse a stale preview or create a second payment after an uncertain response.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "previewId": {
      "type": "string",
      "format": "uuid"
    },
    "idempotencyKey": {
      "type": "string"
    }
  },
  "required": [
    "previewId",
    "idempotencyKey"
  ],
  "additionalProperties": true
}

HTTP 201 response (application/json)

{
  "type": "object",
  "properties": {
    "duplicate": {
      "type": "boolean"
    },
    "change": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "status": {
          "type": "string"
        }
      },
      "additionalProperties": true
    },
    "preview": {
      "type": "object",
      "properties": {
        "effectiveMode": {
          "type": "string"
        }
      },
      "additionalProperties": true
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

POST /api/subscriptions/bundles/cancel

Schedule the supported cancellation at period end.

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • bundleSubscriptionId: integer (required)
Returns
Current cancellation/subscription result.
Side effects
Schedule the supported cancellation at period end. Review the confirmation and retry requirements before repeating the request.
Notes
Cancellation is not a refund. Previously earned keep-products ownership remains unless its source payment is reversed.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "bundleSubscriptionId": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": [
    "bundleSubscriptionId"
  ],
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "subscription": {
      "type": "object",
      "properties": {
        "status": {
          "type": "string"
        },
        "cancel_at_period_end": {
          "type": "boolean"
        }
      },
      "additionalProperties": true
    },
    "immediate": {
      "type": "boolean"
    },
    "alreadyEnded": {
      "type": "boolean"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

POST /api/subscriptions/bundles/reactivate

Resume an eligible plan before the cancellation takes effect.

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • bundleSubscriptionId: integer (required)
Returns
Current subscription/reactivation result.
Side effects
Resume an eligible plan before the cancellation takes effect. Review the confirmation and retry requirements before repeating the request.
Notes
Eligibility is checked against the current plan and provider state.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "bundleSubscriptionId": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": [
    "bundleSubscriptionId"
  ],
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "subscription": {
      "type": "object",
      "properties": {
        "status": {
          "type": "string"
        },
        "cancel_at_period_end": {
          "type": "boolean"
        }
      },
      "additionalProperties": true
    },
    "immediate": {
      "type": "boolean"
    },
    "alreadyEnded": {
      "type": "boolean"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

POST /api/subscriptions/bundles/cancel-scheduled-change

Remove a pending supported plan change.

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • bundleSubscriptionId: integer (required)
Returns
Current scheduled-change result.
Side effects
Remove a pending supported plan change. Review the confirmation and retry requirements before repeating the request.
Notes
This cancels a plan change, not the subscription itself.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "bundleSubscriptionId": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": [
    "bundleSubscriptionId"
  ],
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "canceled": {
      "type": "boolean"
    },
    "changeId": {
      "type": "integer",
      "minimum": 1
    },
    "subscriptionId": {
      "type": "integer",
      "minimum": 1
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

POST /api/subscriptions/bundles/billing-portal

Create an authorized customer billing-portal session.

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • bundleSubscriptionId: integer
  • standaloneSubscriptionId: integer
  • historyOnly: boolean
Returns
200: private url, optional expiresAt for a provider portal, or url and sessionId for payment-method setup. See the conditional behavior below.
Side effects
Create an authorized customer billing-portal session. Review the confirmation and retry requirements before repeating the request.
Notes
Ownership is checked against the signed-in buyer. historyOnly restricts the provider portal to invoice history; it does not update payment methods or cancel a plan.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "bundleSubscriptionId": {
      "type": "integer",
      "minimum": 1
    },
    "standaloneSubscriptionId": {
      "type": "integer",
      "minimum": 1
    },
    "historyOnly": {
      "type": "boolean"
    }
  },
  "additionalProperties": true,
  "oneOf": [
    {
      "required": [
        "bundleSubscriptionId"
      ],
      "not": {
        "required": [
          "standaloneSubscriptionId"
        ]
      }
    },
    {
      "required": [
        "standaloneSubscriptionId"
      ],
      "not": {
        "required": [
          "bundleSubscriptionId"
        ]
      }
    }
  ]
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "format": "uri"
    },
    "sessionId": {
      "type": "string"
    },
    "expiresAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

POST /api/subscriptions/bundles/manual-billing-setup

Begin billing setup for an eligible manually granted subscription.

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • kind: bundle | standalone (required)
  • subscriptionId: integer (required)
  • sessionId: string
  • expectedTotalCents: integer
Returns
201: billing setup-session result.
Side effects
Begin billing setup for an eligible manually granted subscription. Review the confirmation and retry requirements before repeating the request.
Notes
A manual access grant does not mean the customer already has a recurring provider payment arrangement.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "kind": {
      "enum": [
        "bundle",
        "standalone"
      ]
    },
    "subscriptionId": {
      "type": "integer",
      "minimum": 1
    },
    "sessionId": {
      "type": "string"
    },
    "expectedTotalCents": {
      "type": "integer",
      "minimum": 0
    }
  },
  "required": [
    "kind",
    "subscriptionId"
  ],
  "additionalProperties": true
}

HTTP 201 response (application/json)

{
  "type": "object",
  "properties": {
    "url": {
      "type": "string"
    },
    "sessionId": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

POST /api/subscriptions/bundles/manual-billing-preview

Preview billing after the customer completes setup.

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • kind: bundle | standalone (required)
  • subscriptionId: integer (required)
  • sessionId: string (required)
  • expectedTotalCents: integer
Returns
Current billing quote and setup result.
Side effects
Preview billing after the customer completes setup. Review the confirmation and retry requirements before repeating the request.
Notes
Customer ownership and provider setup completion are verified.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "kind": {
      "enum": [
        "bundle",
        "standalone"
      ]
    },
    "subscriptionId": {
      "type": "integer",
      "minimum": 1
    },
    "sessionId": {
      "type": "string"
    },
    "expectedTotalCents": {
      "type": "integer",
      "minimum": 0
    }
  },
  "required": [
    "kind",
    "subscriptionId",
    "sessionId"
  ],
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "success": {
      "type": "boolean"
    },
    "amountCents": {
      "type": "integer",
      "minimum": 0
    },
    "taxAmountCents": {
      "type": "integer",
      "minimum": 0
    },
    "totalCents": {
      "type": "integer",
      "minimum": 0
    },
    "currency": {
      "type": "string"
    },
    "productName": {
      "type": "string"
    },
    "sessionId": {
      "type": "string"
    },
    "subscriptionId": {
      "type": "integer",
      "minimum": 1
    },
    "kind": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

POST /api/subscriptions/bundles/manual-billing-confirm

Confirm the reviewed manual-grant billing amount.

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • kind: bundle | standalone (required)
  • subscriptionId: integer (required)
  • sessionId: string (required)
  • expectedTotalCents: integer (required)
Returns
Billing confirmation result.
Side effects
Confirm the reviewed manual-grant billing amount. Review the confirmation and retry requirements before repeating the request.
Notes
A changed quote is rejected rather than silently charging a different amount.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "kind": {
      "enum": [
        "bundle",
        "standalone"
      ]
    },
    "subscriptionId": {
      "type": "integer",
      "minimum": 1
    },
    "sessionId": {
      "type": "string"
    },
    "expectedTotalCents": {
      "type": "integer",
      "minimum": 0
    }
  },
  "required": [
    "kind",
    "subscriptionId",
    "sessionId",
    "expectedTotalCents"
  ],
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "success": {
      "type": "boolean"
    },
    "amountCents": {
      "type": "integer",
      "minimum": 0
    },
    "taxAmountCents": {
      "type": "integer",
      "minimum": 0
    },
    "totalCents": {
      "type": "integer",
      "minimum": 0
    },
    "currency": {
      "type": "string"
    },
    "productName": {
      "type": "string"
    },
    "sessionId": {
      "type": "string"
    },
    "subscriptionId": {
      "type": "integer",
      "minimum": 1
    },
    "kind": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

GET /api/subscriptions/bundles/customer/subscriptions

List the signed-in customer's subscriptions for one developer.

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • query developerId: integer (required)
Returns
{ subscriptions } or an empty list.
Side effects
Retrieves the authorized resource. Operation-specific notes identify preparation or referral recording where applicable.
Notes
The server resolves the customer from the signed-in user; do not supply another customer identity.
Request, response and error reference

Query and header parameters

[
  {
    "name": "developerId",
    "in": "query",
    "required": true,
    "schema": {
      "type": "integer",
      "minimum": 1
    }
  }
]

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "subscriptions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {},
        "additionalProperties": true
      }
    }
  },
  "required": [
    "subscriptions"
  ],
  "additionalProperties": true
}

Documented failures: 503.

SoundSync

Developer bundle management

GET /api/developer/subscription-bundles

List bundle definitions in the effective developer workspace.

Auth
Account Bearer JWT
Scope
The operation-specific account permissions and resource ownership described below.
Request fields
  • query status: string
Returns
Scoped bundle list.
Side effects
Retrieves the authorized resource. Operation-specific notes identify preparation or referral recording where applicable.
Notes
products.view and all-products scope.
Request, response and error reference

Query and header parameters

[
  {
    "name": "status",
    "in": "query",
    "schema": {
      "type": "string"
    },
    "required": false,
    "description": ""
  }
]

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "bundles": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 1
          },
          "developerId": {
            "type": "integer",
            "minimum": 1
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "billingInterval": {
            "type": "string",
            "enum": [
              "monthly",
              "yearly"
            ]
          },
          "trialDays": {
            "type": "integer"
          },
          "failedPaymentGraceDays": {
            "type": "integer"
          },
          "membershipMode": {
            "type": "string",
            "enum": [
              "selected_products",
              "all_products"
            ]
          },
          "fulfillmentMode": {
            "type": "string",
            "enum": [
              "access",
              "keep_products"
            ]
          },
          "basePriceStrategy": {
            "type": "string",
            "enum": [
              "fixed",
              "percent_discount",
              "fixed_discount"
            ]
          },
          "fixedBundlePriceCents": {
            "type": "integer",
            "minimum": 0
          },
          "discountValue": {
            "type": "number"
          },
          "cancellationBehavior": {
            "type": "string",
            "enum": [
              "access_until_period_end",
              "lock_immediately"
            ]
          },
          "environment": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "activeVersionId": {
            "type": "integer",
            "minimum": 1
          },
          "versions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer",
                  "minimum": 1
                },
                "recurringAmountCents": {
                  "type": "integer",
                  "minimum": 0
                },
                "products": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "productId": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "name": {
                        "type": "string"
                      }
                    },
                    "additionalProperties": true
                  }
                }
              },
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true
      }
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 500, 503.

GET /api/developer/subscription-bundles/:bundleId

Read an owned bundle definition and versions.

Auth
Account Bearer JWT
Scope
The operation-specific account permissions and resource ownership described below.
Returns
{ bundle }.
Side effects
Retrieves the authorized resource. Operation-specific notes identify preparation or referral recording where applicable.
Notes
products.view and all-products scope; another developer's definition is not exposed.
Request, response and error reference

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "bundle": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "developerId": {
          "type": "integer",
          "minimum": 1
        },
        "name": {
          "type": "string"
        },
        "slug": {
          "type": "string"
        },
        "description": {
          "type": [
            "string",
            "null"
          ]
        },
        "status": {
          "type": "string"
        },
        "currency": {
          "type": "string"
        },
        "billingInterval": {
          "type": "string",
          "enum": [
            "monthly",
            "yearly"
          ]
        },
        "trialDays": {
          "type": "integer"
        },
        "failedPaymentGraceDays": {
          "type": "integer"
        },
        "membershipMode": {
          "type": "string",
          "enum": [
            "selected_products",
            "all_products"
          ]
        },
        "fulfillmentMode": {
          "type": "string",
          "enum": [
            "access",
            "keep_products"
          ]
        },
        "basePriceStrategy": {
          "type": "string",
          "enum": [
            "fixed",
            "percent_discount",
            "fixed_discount"
          ]
        },
        "fixedBundlePriceCents": {
          "type": "integer",
          "minimum": 0
        },
        "discountValue": {
          "type": "number"
        },
        "cancellationBehavior": {
          "type": "string",
          "enum": [
            "access_until_period_end",
            "lock_immediately"
          ]
        },
        "environment": {
          "type": "string",
          "enum": [
            "test",
            "live"
          ]
        },
        "activeVersionId": {
          "type": "integer",
          "minimum": 1
        },
        "versions": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer",
                "minimum": 1
              },
              "recurringAmountCents": {
                "type": "integer",
                "minimum": 0
              },
              "products": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "productId": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            },
            "additionalProperties": true
          }
        }
      },
      "additionalProperties": true
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 500, 503.

POST /api/developer/subscription-bundles

Create a bundle definition and version.

Auth
Account Bearer JWT
Scope
The operation-specific account permissions and resource ownership described below.
Request fields
  • id: integer
  • developerId: integer
  • name: string (required)
  • slug: string
  • description: string,null
  • status: string
  • currency: string
  • billingInterval: monthly | yearly
  • trialDays: integer
  • failedPaymentGraceDays: integer
  • membershipMode: selected_products | all_products
  • fulfillmentMode: access | keep_products
  • basePriceStrategy: fixed | percent_discount | fixed_discount
  • fixedBundlePriceCents: integer
  • discountValue: number
  • cancellationBehavior: access_until_period_end | lock_immediately
  • environment: test | live
  • activeVersionId: integer
  • versions: array
  • productIds: array
  • metadata: object. Operation-specific extension data; do not put credentials or unnecessary personal information here.
  • versionMetadata: object. Operation-specific extension data; do not put credentials or unnecessary personal information here.
Returns
201: created bundle/version result.
Side effects
Create a bundle definition and version. Review the confirmation and retry requirements before repeating the request.
Notes
products.edit; selected products must be in scope, and all_products requires all-products scope. Keep discounted paid amounts at least $9.99 before tax. selected_products requires eligible productIds. fixed pricing requires fixedBundlePriceCents; discount strategies require discountValue. Prices remain subject to the paid minimum. Creating a new version does not bypass price notices or migrate existing billing authority.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "minimum": 1
    },
    "developerId": {
      "type": "integer",
      "minimum": 1
    },
    "name": {
      "type": "string"
    },
    "slug": {
      "type": "string"
    },
    "description": {
      "type": [
        "string",
        "null"
      ]
    },
    "status": {
      "type": "string"
    },
    "currency": {
      "type": "string"
    },
    "billingInterval": {
      "type": "string",
      "enum": [
        "monthly",
        "yearly"
      ]
    },
    "trialDays": {
      "type": "integer"
    },
    "failedPaymentGraceDays": {
      "type": "integer"
    },
    "membershipMode": {
      "type": "string",
      "enum": [
        "selected_products",
        "all_products"
      ]
    },
    "fulfillmentMode": {
      "type": "string",
      "enum": [
        "access",
        "keep_products"
      ]
    },
    "basePriceStrategy": {
      "type": "string",
      "enum": [
        "fixed",
        "percent_discount",
        "fixed_discount"
      ]
    },
    "fixedBundlePriceCents": {
      "type": "integer",
      "minimum": 0
    },
    "discountValue": {
      "type": "number"
    },
    "cancellationBehavior": {
      "type": "string",
      "enum": [
        "access_until_period_end",
        "lock_immediately"
      ]
    },
    "environment": {
      "type": "string",
      "enum": [
        "test",
        "live"
      ]
    },
    "activeVersionId": {
      "type": "integer",
      "minimum": 1
    },
    "versions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 1
          },
          "recurringAmountCents": {
            "type": "integer",
            "minimum": 0
          },
          "products": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "productId": {
                  "type": "integer",
                  "minimum": 1
                },
                "name": {
                  "type": "string"
                }
              },
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true
      }
    },
    "productIds": {
      "type": "array",
      "items": {
        "type": "integer",
        "minimum": 1
      }
    },
    "metadata": {
      "type": "object",
      "additionalProperties": true,
      "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
    },
    "versionMetadata": {
      "type": "object",
      "additionalProperties": true,
      "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
    }
  },
  "additionalProperties": true,
  "required": [
    "name"
  ]
}

HTTP 201 response (application/json)

{
  "type": "object",
  "properties": {
    "bundle": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "developerId": {
          "type": "integer",
          "minimum": 1
        },
        "name": {
          "type": "string"
        },
        "slug": {
          "type": "string"
        },
        "description": {
          "type": [
            "string",
            "null"
          ]
        },
        "status": {
          "type": "string"
        },
        "currency": {
          "type": "string"
        },
        "billingInterval": {
          "type": "string",
          "enum": [
            "monthly",
            "yearly"
          ]
        },
        "trialDays": {
          "type": "integer"
        },
        "failedPaymentGraceDays": {
          "type": "integer"
        },
        "membershipMode": {
          "type": "string",
          "enum": [
            "selected_products",
            "all_products"
          ]
        },
        "fulfillmentMode": {
          "type": "string",
          "enum": [
            "access",
            "keep_products"
          ]
        },
        "basePriceStrategy": {
          "type": "string",
          "enum": [
            "fixed",
            "percent_discount",
            "fixed_discount"
          ]
        },
        "fixedBundlePriceCents": {
          "type": "integer",
          "minimum": 0
        },
        "discountValue": {
          "type": "number"
        },
        "cancellationBehavior": {
          "type": "string",
          "enum": [
            "access_until_period_end",
            "lock_immediately"
          ]
        },
        "environment": {
          "type": "string",
          "enum": [
            "test",
            "live"
          ]
        },
        "activeVersionId": {
          "type": "integer",
          "minimum": 1
        },
        "versions": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer",
                "minimum": 1
              },
              "recurringAmountCents": {
                "type": "integer",
                "minimum": 0
              },
              "products": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "productId": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            },
            "additionalProperties": true
          }
        }
      },
      "additionalProperties": true
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 500, 503.

PUT /api/developer/subscription-bundles/:bundleId

Update an owned bundle through the versioned definition workflow.

Auth
Account Bearer JWT
Scope
The operation-specific account permissions and resource ownership described below.
Request fields
  • id: integer
  • developerId: integer
  • name: string (required)
  • slug: string
  • description: string,null
  • status: string
  • currency: string
  • billingInterval: monthly | yearly
  • trialDays: integer
  • failedPaymentGraceDays: integer
  • membershipMode: selected_products | all_products
  • fulfillmentMode: access | keep_products
  • basePriceStrategy: fixed | percent_discount | fixed_discount
  • fixedBundlePriceCents: integer
  • discountValue: number
  • cancellationBehavior: access_until_period_end | lock_immediately
  • environment: test | live
  • activeVersionId: integer
  • versions: array
  • productIds: array
  • metadata: object. Operation-specific extension data; do not put credentials or unnecessary personal information here.
  • versionMetadata: object. Operation-specific extension data; do not put credentials or unnecessary personal information here.
Returns
Updated bundle/version result.
Side effects
Update an owned bundle through the versioned definition workflow. Review the confirmation and retry requirements before repeating the request.
Notes
products.edit and relevant product scope. Do not assume changing the catalog rewrites every prior paid-invoice ownership snapshot. Creates/updates the supported versioned plan. Customer history can lock the fulfillment model and environment. Product-managed plans must be edited from product setup. Changing monthly/yearly cadence with active, trialing, past_due or unpaid standalone or linked bundle contracts returns 409 SUBSCRIPTION_BILLING_INTERVAL_LOCKED. Keep existing contracts on their cadence and create a separate offer for the new interval.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "minimum": 1
    },
    "developerId": {
      "type": "integer",
      "minimum": 1
    },
    "name": {
      "type": "string"
    },
    "slug": {
      "type": "string"
    },
    "description": {
      "type": [
        "string",
        "null"
      ]
    },
    "status": {
      "type": "string"
    },
    "currency": {
      "type": "string"
    },
    "billingInterval": {
      "type": "string",
      "enum": [
        "monthly",
        "yearly"
      ]
    },
    "trialDays": {
      "type": "integer"
    },
    "failedPaymentGraceDays": {
      "type": "integer"
    },
    "membershipMode": {
      "type": "string",
      "enum": [
        "selected_products",
        "all_products"
      ]
    },
    "fulfillmentMode": {
      "type": "string",
      "enum": [
        "access",
        "keep_products"
      ]
    },
    "basePriceStrategy": {
      "type": "string",
      "enum": [
        "fixed",
        "percent_discount",
        "fixed_discount"
      ]
    },
    "fixedBundlePriceCents": {
      "type": "integer",
      "minimum": 0
    },
    "discountValue": {
      "type": "number"
    },
    "cancellationBehavior": {
      "type": "string",
      "enum": [
        "access_until_period_end",
        "lock_immediately"
      ]
    },
    "environment": {
      "type": "string",
      "enum": [
        "test",
        "live"
      ]
    },
    "activeVersionId": {
      "type": "integer",
      "minimum": 1
    },
    "versions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 1
          },
          "recurringAmountCents": {
            "type": "integer",
            "minimum": 0
          },
          "products": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "productId": {
                  "type": "integer",
                  "minimum": 1
                },
                "name": {
                  "type": "string"
                }
              },
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true
      }
    },
    "productIds": {
      "type": "array",
      "items": {
        "type": "integer",
        "minimum": 1
      }
    },
    "metadata": {
      "type": "object",
      "additionalProperties": true,
      "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
    },
    "versionMetadata": {
      "type": "object",
      "additionalProperties": true,
      "description": "Operation-specific extension data; do not put credentials or unnecessary personal information here."
    }
  },
  "additionalProperties": true,
  "required": [
    "name"
  ]
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "bundle": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "developerId": {
          "type": "integer",
          "minimum": 1
        },
        "name": {
          "type": "string"
        },
        "slug": {
          "type": "string"
        },
        "description": {
          "type": [
            "string",
            "null"
          ]
        },
        "status": {
          "type": "string"
        },
        "currency": {
          "type": "string"
        },
        "billingInterval": {
          "type": "string",
          "enum": [
            "monthly",
            "yearly"
          ]
        },
        "trialDays": {
          "type": "integer"
        },
        "failedPaymentGraceDays": {
          "type": "integer"
        },
        "membershipMode": {
          "type": "string",
          "enum": [
            "selected_products",
            "all_products"
          ]
        },
        "fulfillmentMode": {
          "type": "string",
          "enum": [
            "access",
            "keep_products"
          ]
        },
        "basePriceStrategy": {
          "type": "string",
          "enum": [
            "fixed",
            "percent_discount",
            "fixed_discount"
          ]
        },
        "fixedBundlePriceCents": {
          "type": "integer",
          "minimum": 0
        },
        "discountValue": {
          "type": "number"
        },
        "cancellationBehavior": {
          "type": "string",
          "enum": [
            "access_until_period_end",
            "lock_immediately"
          ]
        },
        "environment": {
          "type": "string",
          "enum": [
            "test",
            "live"
          ]
        },
        "activeVersionId": {
          "type": "integer",
          "minimum": 1
        },
        "versions": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer",
                "minimum": 1
              },
              "recurringAmountCents": {
                "type": "integer",
                "minimum": 0
              },
              "products": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "productId": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            },
            "additionalProperties": true
          }
        }
      },
      "additionalProperties": true
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 500, 409, 503.

Operation errors: SUBSCRIPTION_BILLING_INTERVAL_LOCKED.

PATCH /api/developer/subscription-bundles/:bundleId

Change bundle publication/status.

Auth
Account Bearer JWT
Scope
The operation-specific account permissions and resource ownership described below.
Request fields
  • status: draft | published | archived (required)
Returns
Current bundle/status result.
Side effects
Change bundle publication/status. Review the confirmation and retry requirements before repeating the request.
Notes
products.edit and all-products scope.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "enum": [
        "draft",
        "published",
        "archived"
      ]
    }
  },
  "required": [
    "status"
  ],
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "bundle": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "developerId": {
          "type": "integer",
          "minimum": 1
        },
        "name": {
          "type": "string"
        },
        "slug": {
          "type": "string"
        },
        "description": {
          "type": [
            "string",
            "null"
          ]
        },
        "status": {
          "type": "string"
        },
        "currency": {
          "type": "string"
        },
        "billingInterval": {
          "type": "string",
          "enum": [
            "monthly",
            "yearly"
          ]
        },
        "trialDays": {
          "type": "integer"
        },
        "failedPaymentGraceDays": {
          "type": "integer"
        },
        "membershipMode": {
          "type": "string",
          "enum": [
            "selected_products",
            "all_products"
          ]
        },
        "fulfillmentMode": {
          "type": "string",
          "enum": [
            "access",
            "keep_products"
          ]
        },
        "basePriceStrategy": {
          "type": "string",
          "enum": [
            "fixed",
            "percent_discount",
            "fixed_discount"
          ]
        },
        "fixedBundlePriceCents": {
          "type": "integer",
          "minimum": 0
        },
        "discountValue": {
          "type": "number"
        },
        "cancellationBehavior": {
          "type": "string",
          "enum": [
            "access_until_period_end",
            "lock_immediately"
          ]
        },
        "environment": {
          "type": "string",
          "enum": [
            "test",
            "live"
          ]
        },
        "activeVersionId": {
          "type": "integer",
          "minimum": 1
        },
        "versions": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer",
                "minimum": 1
              },
              "recurringAmountCents": {
                "type": "integer",
                "minimum": 0
              },
              "products": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "productId": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            },
            "additionalProperties": true
          }
        }
      },
      "additionalProperties": true
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 500, 503.

GET /api/developer/subscription-bundles/reporting/summary

Read recurring bundle reporting totals.

Auth
Account Bearer JWT
Scope
The operation-specific account permissions and resource ownership described below.
Returns
{ summary } with active_subscriptions, recurring_amount_cents, past_due_subscriptions.
Side effects
Retrieves the authorized resource. Operation-specific notes identify preparation or referral recording where applicable.
Notes
products.view and all-products scope. Recurring amount is not a confirmed payout or proof every invoice was paid.
Request, response and error reference

No request body fields are read by this operation.

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "summary": {
      "type": "object",
      "properties": {
        "active_subscriptions": {
          "type": "integer"
        },
        "recurring_amount_cents": {
          "type": "integer",
          "minimum": 0
        },
        "past_due_subscriptions": {
          "type": "integer"
        }
      },
      "additionalProperties": true
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 500, 503.

SoundSync

Illustrative customer cancellation

Illustrative customer cancellation
curl -X POST "https://www.soundsync.dev/api/subscriptions/bundles/cancel" \
  -H "Authorization: Bearer CUSTOMER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"bundleSubscriptionId":123}'

Access, ownership, and payment are separate states

An access plan grants recurring use while its entitlement is valid. A keep_products plan records product ownership from eligible verified paid invoices and their catalog snapshots. An unpaid trial or a manually supplied active state is not permanent paid ownership. Canceling future renewal does not remove ownership earned from an unreversed prior payment.

The standard refund policy covers the initial subscription payment within 14 days, not renewals. Original-payment reversals reconcile corresponding financial shares and product access. Independent purchases and unrelated ownership grants must not be erased.

Errors and environment checks

Errors use code and message with field or conflicts where supplied. Relevant rejection codes include subscription_bundle_already_active, subscription_bundle_change_recommended, subscription_bundle_payment_required, subscription_bundle_environment_mismatch, and subscription_bundle_live_required. Inspect the operation result before retrying. These are source-backed contracts; the example is illustrative rather than a captured provider transcript.

SoundSync

Customer payment and standalone cancellation

POST /api/subscriptions/bundles/payment-update

Start payment-method setup

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • bundleSubscriptionId: integer
  • standaloneSubscriptionId: integer
  • authenticate: boolean
Returns
Card setup returns { url, sessionId }; invoice authentication returns { url }. Open the returned provider URL. Completion is verified by the signed provider callback, not by the browser return alone.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "bundleSubscriptionId": {
      "type": "integer",
      "minimum": 1
    },
    "standaloneSubscriptionId": {
      "type": "integer",
      "minimum": 1
    },
    "authenticate": {
      "type": "boolean"
    }
  },
  "additionalProperties": true,
  "oneOf": [
    {
      "required": [
        "bundleSubscriptionId"
      ],
      "not": {
        "required": [
          "standaloneSubscriptionId"
        ]
      }
    },
    {
      "required": [
        "standaloneSubscriptionId"
      ],
      "not": {
        "required": [
          "bundleSubscriptionId"
        ]
      }
    }
  ]
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "format": "uri"
    },
    "sessionId": {
      "type": "string"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

POST /api/subscriptions/bundles/cancel-standalone

Cancel standalone renewal at paid-period end

Auth
Account Bearer JWT
Scope
The signed-in buyer's subscription and authorized provider billing relationship.
Request fields
  • standaloneSubscriptionId: integer (required)
Returns
{ subscription }; an ended subscription can return alreadyEnded: true, with duplicate: true when already ended locally. Inspect paid-through access and the returned state rather than treating every response as a new cancellation.
Request, response and error reference

Request (application/json)

{
  "type": "object",
  "properties": {
    "standaloneSubscriptionId": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": [
    "standaloneSubscriptionId"
  ],
  "additionalProperties": true
}

HTTP 200 response (application/json)

{
  "type": "object",
  "properties": {
    "subscription": {
      "type": "object",
      "properties": {},
      "additionalProperties": true
    },
    "duplicate": {
      "type": "boolean"
    },
    "alreadyEnded": {
      "type": "boolean"
    }
  },
  "additionalProperties": true
}

Documented failures: 400, 403, 404, 409, 503.

SoundSync

Lifecycle behavior

Renewals and recovery

30-day price notices, 14-day recovery, and permanent ownership.

SoundSync

Two subscription models

Selected products versus all products describes bundle membership, not a third ownership model. A trial, unpaid invoice, or manually granted access is not proof of permanent paid ownership.

ModelOffer and API valueWhat the customer keeps
Own the productsSubscription bundle with fulfillmentMode: keep_productsQualifying paid invoices earn permanent ownership under the plan. Ending future billing does not erase already earned products; a reversal of the qualifying payment can reverse that ownership.
Lease the productsStandalone product subscription, or subscription bundle with fulfillmentMode: accessRecurring access while entitled. Cancellation preserves the paid-through period. An unresolved recovery suspends the license while billing continues; cancel separately to stop future renewals. Separate purchases are unaffected.

A fixed payment-recovery window

An unpaid renewal starts a 14-day recovery episode. Reminder opportunities fall on days 0, 5, 10, and 13, subject to eligibility and delivery readiness. Customers can update their payment method from their account. Eligible automatic collection attempts remain subject to provider limits and the current payment state; this is not a promise of a charge attempt every day.

Rental suspension is not subscription cancellation

After the 14-day recovery deadline, an unpaid rental/access subscription has its license suspended, not automatically canceled. The subscription and billing continue, further renewal invoices can arise, and eligible recovery of outstanding payments continues. Suspension does not stop billing or forgive unpaid invoices. Customers who want to stop future renewals must use the supported cancellation action; cancellation does not erase an existing balance.

Rental access is restored after provider-confirmed payment and reconciliation of all outstanding invoices. A newer paid invoice alone does not clear older arrears. Closed or uncertain invoices can require billing review rather than automatic restoration.

A keep-your-products plan has a different outcome: an unresolved renewal can end the recurring subscription after reconciliation, while products already earned from unreversed qualifying payments remain owned. No new permanent products are earned from an unpaid invoice. Pending or uncertain payments are reconciled before another collection attempt; do not start another checkout to bypass recovery. Plugin enforcement follows the installed package and its signed offline/online rules, not an instant remote change to every distributed binary.

Settings and effective recovery are different

The product-settings endpoint accepts legacy failedPaymentGraceDays values 0, 3, 7, and 14. New settings default to 14. The managed renewal/recovery policy uses the server-issued 14-day episode and returned access state; do not shorten or extend it in a client from a legacy setting. Payment-recovery grace and a plugin package's offline grace are different clocks.

SoundSync

Use the contract for the right action

Bundle and customer actions

Bundle configuration, checkout, changes, cancellation, and payment-method setup.

Checkout API

Standalone product checkout with Stripe subscriptions.

Renewal and recovery

Regular pricing, notices, payment failures, and access.

Current access and previous records

The customer account separates current licenses and plans from Previous licenses and Previous subscriptions. Ended or inactive records stay available in expandable history; a plan with remaining eligible paid-through access is not hidden merely because cancellation is scheduled.

A signed-in marketplace customer who already owns a product can open their library and downloads from its product page. This is an account-specific lookup, not a public ownership list. License-reset cooldowns show the next eligible date; resetting activations does not create another license.

Plan changes appear only when eligible

The account offers bundle plan changes for eligible active or trialing lease plans without a scheduled cancellation. Ended plans, keep-your-products plans, and plans already scheduled to cancel do not offer the same plan-change controls. Payment recovery and plan-conflict checks still apply. Use the returned eligibility and preview rather than constructing a plan change from a hidden control.

History, payment records, cancellation, and paid-through access are different states. Retained billing history is not an invitation to restart a canceled plan or charge a saved card without authorization.

Invoice history is not payment-method setup

Send historyOnly: true to billing-portal with exactly one customer-owned bundleSubscriptionId or standaloneSubscriptionId. The returned url opens an invoice-history portal with payment-method changes, customer changes, subscription changes, and cancellation disabled.

Without historyOnly, a subscription using the fourteen_days_v1 recovery policy is routed to payment-method setup and can return url plus sessionId. Older supported records can use their permitted billing portal. Use payment-update with authenticate: true for a currently outstanding invoice authentication URL, not historyOnly or a general billing portal. A portal URL is private and does not prove payment or grant product access.

SoundSync

Open subscription invoice history

Open subscription invoice history
curl --fail-with-body -sS -X POST "$SOUNDSYNC_API_BASE/api/subscriptions/bundles/billing-portal" \
  -H "Authorization: Bearer $CUSTOMER_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"bundleSubscriptionId":123,"historyOnly":true}'

Keep existing subscribers on their billing interval

Monthly and annual offers use different billing cadences. If a standalone subscription or a linked subscription bundle has active, trialing, past_due or unpaid contracts, changing its monthly/yearly interval is blocked. Create a separate offer for the new cadence rather than changing the period beneath current subscribers.

The settings and versioned-bundle APIs return 409 SUBSCRIPTION_BILLING_INTERVAL_LOCKED for this condition. Reload the existing offer and retain its interval. This guard does not convert ownership models, cancel existing subscriptions or migrate payment-provider billing authority.

Permanent gifts and paid subscription ownership

A timed manual keep-products grant gives the customer permanent ownership of the products included when the developer makes that grant, without a payment. That original product snapshot remains owned after subscription cancellation, expiry or incomplete billing setup. Explicit developer access removal can revoke the grant. New products added later are not retroactively included.

A lifetime manual grant instead provides complimentary ongoing subscription access; it is not the timed-grant ownership model or evidence of a paid invoice. Ordinary access-only grants and unpaid checkout trials also do not create paid ownership.

Paid keep-products invoices establish their own product snapshots. Refunding or reversing a paid purchase affects that purchase, not an independent manual ownership grant. Future paid subscription benefits still require the separate billing setup, preview and customer confirmation; a gift is not permission to charge a card.