API Reference

A human-readable summary of all Subscription Service API endpoints. For the full interactive OpenAPI specification with request/response schemas, see API Specification.

Overview

All endpoints are served by the site-subscriptions API Gateway Lambda at https://subscription-service.api.floatme.io/v1.

Authorization is enforced at the API Gateway level using AWS Signature Version 4 (IAM):

  • IAM (awsSigV4) — all callers authenticate with AWS IAM credentials. The Authorization header carries the AWS SigV4 signature. Mobile clients reach this service via the internal FloatMe API gateway, which proxies requests with its own IAM identity.

All user-scoped paths are prefixed with /{user_id}, scoping every operation to a single user record.

Path Conventions

Convention Example

User-scoped path

/{user_id}/subscriptions/activate

Subscription-scoped path

/{user_id}/subscriptions/{subscription_id}/pay

Environment-restricted path

/qa/subscriptions/create (test env only)

Subscription Management

Endpoints for creating, retrieving, and reactivating subscriptions.

Method Path Description

PUT

/{user_id}/subscriptions/activate

Create the user’s first scheduled subscription record. Idempotent — returns 200 without error if a SCHEDULED subscription already exists. Sets the billing date using the next Monday (or next Friday when Friday billing is enabled) after a 9-day trial grace period. Default amount is $4.99.

POST

/{user_id}/subscriptions/reactivate

Reactivate a lapsed subscription. Immediately charges the user via pinless debit for the current period, writes a COMPLETED record for that charge, and schedules the next billing period. No customer receipt is sent from this endpoint. Returns 201 with the new subscription and masked card number on success.

GET

/{user_id}/subscriptions

Return all subscription records for the user, ordered ascending by subscription date. Returns 404 if no records exist.

GET

/{user_id}/subscriptions/current

Return the active subscription most relevant to the mobile app — either the upcoming scheduled subscription or the most recently past-due one. Applies status normalisation: PAUSED_SKIPPEDPAUSED, ACHSENTPENDING, ERROR (same day) → SCHEDULED, ERROR (past due) → PAST_DUE. Returns STALE if the subscription due date is older than the configured OLD_SUBSCRIPTION_DAYS threshold.

PATCH

/{user_id}/subscriptions

Support-tool update for a subscription record. Allows overriding subscription_date and subscription_status. Writes an AdminUpdate history entry (process: support). Returns 202 on success.

POST /{user_id}/subscriptions/reactivate — Request Body

The request body is optional. If omitted (or empty), the base/v0 tier defaults are used.

Field Type Required Description

tier

string

Conditional

Membership tier name (e.g., base, plus). Required if version is supplied.

version

string

Conditional

Tier version string (e.g., v0, v1). Required if tier is supplied.

Both tier and version must be present together or both absent. Tier and version are validated against the global.tiers.config GrowthBook feature flag for the user.

POST /{user_id}/subscriptions/reactivate — Responses

Status Schema Meaning

200 OK

(empty)

User already has a SCHEDULED subscription — no action taken.

201 Created

ReactivationResult

Payment succeeded. Body contains the new scheduled subscription and the masked debit card number.

402 Payment Required

ErrorResponse

The pinless debit charge failed (payment provider returned a failure). User must update their payment method before retrying.

409 Conflict

ErrorResponse

The user’s debit card is invalid.

500 Internal Server Error

ErrorResponse

Unexpected error (GrowthBook unavailable, DynamoDB write failure, etc.).

PATCH /{user_id}/subscriptions — Request Body

Field Type Required Description

subscription_id

string

Yes

ID of the subscription record to update.

subscription_date

string (RFC 3339)

No

New billing due date. Must be a valid RFC 3339 timestamp. If omitted, the existing date is kept.

subscription_status

string

No

New status value. If omitted, the existing status is kept.

Billing

Endpoints for manual payment, billing history, billing details, and active term length.

Method Path Description

POST

/{user_id}/subscriptions/{subscription_id}/pay

Manually pay a specific subscription via pinless debit. The user must be ACTIVE, the subscription must be in SCHEDULED or ERROR status, and the subscription must not be older than the configured stale threshold. Acquires a DynamoDB distributed lock before billing. On success, schedules the next subscription and sends a Segment/AppsFlyer receipt. Returns 201 on success.

GET

/{user_id}/subscriptions/{subscription_id}/history

Return all history entries for a specific subscription record from the billing-activity-history DynamoDB table. Returns an array of Subscription objects representing each state the record has passed through.

GET

/{user_id}/subscriptions/billing_details

Return billing details for display in the mobile app. If a SCHEDULED subscription exists, returns its amount and date. Otherwise calculates the prospective next billing date and returns the default amount ($4.99). Includes whether Friday billing is enabled and the billing week number within the month.

GET

/{user_id}/subscriptions/active_term

Return the count of consecutive paid months in the user’s current active term, plus the full subscription list. Counts COMPLETED and WAIVED records; stops at PAUSED, PAUSED_SKIPPED, or CANCELLED. ERROR records within the 20-day grace period are counted; those outside it break the term.

POST /{user_id}/subscriptions/{subscription_id}/pay — Path Parameters

Parameter Type Description

user_id

string

ID of the user whose subscription is being paid.

subscription_id

string

ID of the specific subscription record to charge. Must be in SCHEDULED or ERROR status.

GET /{user_id}/subscriptions/billing_details — Response Fields

Field Type Description

amount

string

Subscription amount as a decimal string (e.g., "4.99").

billing_date

string (RFC 3339)

Next scheduled billing date.

friday_billing_enabled

boolean

Whether the Friday billing schedule is active for this environment.

billing_week

integer

Week-of-month number (1–5) for the billing Friday. Present only when Friday billing is enabled.

GET /{user_id}/subscriptions/active_term — Response Fields

Field Type Description

length

integer

Number of consecutive paid months in the current active term.

subscriptions

array of Subscription

All subscription records for the user in reverse chronological order (newest first).

Plan Management

Endpoints for changing a user’s membership tier on their next scheduled billing.

Method Path Description

POST

/{user_id}/subscriptions/upgrade

Upgrade the user’s next scheduled subscription to a higher membership tier. Finds the current SCHEDULED subscription and rewrites its amount and tier name. Clears any pending-downgrade flag. Returns the updated subscription on 201. Returns 404 if no scheduled subscription exists.

POST

/{user_id}/subscriptions/downgrade

Downgrade the user’s next scheduled (or paused) subscription to a lower membership tier. Accepts SCHEDULED or PAUSED status on the next subscription. Sets the is_pending_downgrade flag on the record. Returns the updated subscription on 201. Returns 404 if no eligible subscription exists.

Both upgrade and downgrade validate the requested tier and version against the global.tiers.config GrowthBook feature flag before writing. The amount is updated to the tier’s monthly price from the GrowthBook config.

POST /{user_id}/subscriptions/upgrade — Request Body

Field Type Required Description

upgrade_tier

string

Yes

Name of the tier to upgrade to (e.g., plus). Must match a key in the global.tiers.config GrowthBook feature.

upgrade_tier_version

string

Yes

Version of the tier to upgrade to (e.g., v1). Must match a version_name within the tier config.

POST /{user_id}/subscriptions/downgrade — Request Body

Field Type Required Description

downgrade_tier

string

Yes

Name of the tier to downgrade to (e.g., base). Must match a key in the global.tiers.config GrowthBook feature.

downgrade_tier_version

string

Yes

Version of the tier to downgrade to (e.g., v0). Must match a version_name within the tier config.

Upgrade / Downgrade Responses

Status Schema Meaning

201 Created

Subscription

Tier change applied. Body contains the updated subscription record.

400 Bad Request

ErrorResponse

The requested tier or version is not valid per GrowthBook config.

404 Not Found

ErrorResponse

No eligible scheduled (or paused) subscription found for the user.

500 Internal Server Error

ErrorResponse

Unexpected internal error.

Administration

Endpoints restricted to specific callers or environments.

Method Path Description

POST

/{user_id}/subscriptions/ban

Handle a user ban by cancelling any pending subscriptions. Finds all subscriptions in SCHEDULED or ERROR status and sets each to CANCELLED with updated_event: user_banned. Subscriptions in all other statuses are left unchanged. No request body required. Returns 200 whether or not any records were updated.

PUT

/qa/subscriptions/create

(Test environment only) Batch-create subscription records and history entries for QA automation. Restricted to the test-qa-api IAM caller identity in the test environment. Writes directly to DynamoDB via QAAddAllSubRecords and history.AddAll.

PUT /qa/subscriptions/create — Request Body

Field Type Required Description

records

array of QASubscription

Yes

Subscription records to create. Each entry may include a history array of nested QASubscription entries that will be written to the history table.

Each QASubscription in records requires: user_id, subscription_id, subscription_date, subscription_amount, subscription_status, subscription_period, last_run_date, initial_run_date, completion_date, created_date, and history.

Optional fields per record: transaction_id, usio_error, process, updated_event, term, return_code.

PUT /qa/subscriptions/create — Responses

Status Meaning 201 Created

All records written successfully.

403 Forbidden

Caller is not test-qa-api or the environment is not test.

422 Unprocessable Entity

Request body could not be decoded or a record failed parsing.

500 Internal Server Error

Shared Schemas

Subscription

The core subscription record returned by most read endpoints.

Field Type Required Description

user_id

string

Yes

Owner of the subscription.

subscription_id

string

Yes

Unique identifier for this subscription record.

subscription_date

string

Yes

Billing due date (RFC 3339).

subscription_amount

string

Yes

Amount as a decimal string (e.g., "4.99").

subscription_status

string

Yes

Current status. See Subscription Lifecycle for all values.

subscription_period

string

Yes

Billing period in MM/YYYY format.

created_date

string

Yes

Timestamp when the record was created (RFC 3339).

transaction_id

string

No

Payment confirmation ID from the payments provider.

usio_error

string

No

Error message from the payments provider when a collection attempt fails.

initial_run_date

string

No

Timestamp of the first collection attempt (RFC 3339).

completion_date

string

No

Timestamp when the subscription was marked COMPLETED (RFC 3339).

last_run_date

string

No

Timestamp of the most recent write to this record (RFC 3339).

process

string

No

What process last wrote this record (e.g., manual_repayment, support, scheduled).

updated_event

string

No

Event that triggered the last status change (e.g., user_banned, user_reactivated).

term

string

No

Billing term (monthly).

is_pending_downgrade

boolean

No

true when a downgrade has been scheduled for the next billing cycle.

CurrentSubscriptionDetails

Returned by GET /{user_id}/subscriptions/current.

Field Type Required Description

subscription_id

string

Yes

ID of the current subscription record.

due_date

string (date)

Yes

Due date in YYYY-MM-DD format.

amount

string

Yes

Subscription amount as a decimal string.

status

string

Yes

Normalised display status: SCHEDULED, PENDING, PAUSED, PAST_DUE, COMPLETED, WAIVED, STALE, or CANCELLED.

next_due_date

string (date)

Yes

Due date of the next upcoming subscription record (empty string if none).

outside_grace_period

boolean

Yes

true if the due date is more than 20 days in the past.

paid_in_advance

boolean

Yes

true if the subscription was paid manually before its due date.

grace_period_date

string (date)

Yes

Date when the 20-day grace period expires (YYYY-MM-DD). Empty for completed subscriptions.

grace_period_length

integer

Yes

Length of the grace period in days (always 20).

ErrorResponse

Returned on all error responses.

Field Type Description

message

string

Human-readable description of the error.

code

integer

Optional numeric error code from the payments provider or internal subsystem.

Error Responses

All error responses use the ErrorResponse schema:

{
  "message": "human-readable description",
  "code": 402
}

Common HTTP Status Codes

HTTP Status Meaning Typical Cause

200 OK

Success (no-op)

Idempotent operations where the desired state already exists (e.g., subscription already scheduled).

201 Created

Resource created or payment completed

New subscription written, reactivation charge succeeded, QA records written.

202 Accepted

Update accepted

Returned by PATCH /{user_id}/subscriptions.

400 Bad Request

Invalid request body

Malformed JSON, missing required fields, or invalid tier/version combination.

402 Payment Required

Charge declined

Pinless debit rejected by the payments provider during reactivation.

403 Forbidden

Unauthorised caller

Environment mismatch or caller identity check failed (/qa/ endpoint only).

404 Not Found

Resource not found

No subscription records exist for the user, or the target subscription was not found.

409 Conflict

State conflict

Subscription is in the wrong status for the requested operation, the subscription is too old, or the debit card is invalid.

422 Unprocessable Entity

Parse error

Request body decoded but record fields could not be parsed (e.g., invalid date format).

500 Internal Server Error

Unexpected error

DynamoDB failure, external service unavailable, or unhandled exception.