Skip to main content
PATCH
Update Subscription
Subscription TransitionThe fields billing_cycles and availability.finish_at have an impact on each other. If both are completed during the subscription creation, it will transition to the COMPLETED state upon reaching the nearest event defined in these fields, whether it is the billing cycle or the corresponding finish_at. They are independent end conditions: whichever is reached first completes the subscription.When the availability object is included, finish_at is applied as sent: omit it or send it as null and any previously set end date is removed, so the subscription becomes open-ended and keeps renewing until it is canceled. This means that when you update availability.start_at, you must include finish_at in the same request if you want to keep the end date. start_at does not behave this way: omitting it leaves the current start date unchanged. Omitting the whole availability object leaves both dates unchanged.Note that availability.start_at can only be moved earlier on update. Sending a later date returns a 400 error: The availability start date cannot be greater than the current availability start date.
Retroactive Credential UpdateIf you have existing subscriptions that are failing rebills because of missing credential usage (e.g. from an initial CIT payment), you can use this PATCH endpoint to set payment_method.card.usage retroactively to fix the issue.
Scheduled Plan ChangesUse the plan_change block to move a subscription to another plan effective at its next billing cycle — no proration, no mid-cycle charge. The subscription must be ACTIVE or TRIALING and already on a plan; the target plan must be active and only one change can be pending at a time. Send "plan_change": null to undo a pending change; omitting the field never touches it. The pending change is returned as pending_plan_change on this endpoint and on the subscription object, and the subscription.plan_change_scheduled, subscription.plan_change_canceled and subscription.plan_changed webhooks track its lifecycle.amount, frequency, billing_date and country are managed by the plan and cannot be sent for a plan-linked subscription — combining any of them with plan_change returns 400.
Immediate Plan ChangesSend "effective": "IMMEDIATE" inside plan_change to apply the move in this call instead of scheduling it: the subscription keeps its id, the new plan’s price bills from the next charge, and — unlike a scheduled change — this works even in the final billing cycle. An immediate change while another change is pending returns 409 PLAN_CHANGE_ALREADY_SCHEDULED; undo the pending one first with "plan_change": null.billing_cycle_behavior decides what happens to the billing anchor when the plan changes in place:
  • KEEP (default — omitting the field behaves identically) — the next charge date is untouched; only the price changes.
  • RESTART — the next charge re-anchors to the moment of the change plus the new plan’s frequency. Only the anchor resets: billing cycle counters, the retry configuration and the subscription’s status all survive. RESTART requires effective: IMMEDIATE and returns 400 with a scheduled change; KEEP alongside a scheduled change is accepted and simply schedules the change as usual — a scheduled change never moves the anchor anyway.
An immediate change emits the subscription.plan_changed webhook at the moment of the call — subscription.plan_change_scheduled never fires for it, and pending_plan_change stays null throughout.billing_cycle_behavior is write-only — it is accepted on the request and never echoed in any response.On a monthly subscription created without an explicit frequency.monthly_billing_day, RESTART sets that field to the day of the restart, and every future renewal reads it — restarting on the 15th bills on the 15th from then on, not only for the next cycle. A subscription that already declares a billing day keeps it: the date does not move to the restart day, only the time of day is re-anchored. The value is returned on the subscription object, so it can be read back after the change. The same rule applies to RESTART on Resume Subscription — it is one rule, on both endpoints.
Restarting the Billing Cycle Without a PlanFor a subscription that is not linked to a plan, billing_cycle_behavior is available as a top-level field on this endpoint, typically combined with a frequency change in the same call — for example, moving a subscriber from monthly to yearly and restarting the cycle from today. The same KEEP/RESTART semantics apply, and on a subscription that has not started yet the re-anchored date clamps to its availability.start_at.Placement rules: on a plan-linked subscription the top-level field returns 400 (the anchor is managed through plan_change), and combining the top-level field with a plan_change block returns 400 — use plan_change.billing_cycle_behavior instead. A PAUSED subscription returns 409 INVALID_SUBSCRIPTION_STATUS: a restart is accepted while the subscription is ACTIVE, TRIALING or PAST_DUE — or while it is still CREATED, where (unlike plan_change) the request is allowed and the re-anchored date clamps to availability.start_at. A top-level restart emits no webhook — read the new anchor from the subscription object.

Plan change errors

Each of the 409 and 422 errors below carries a current_status field alongside code and messages, holding the subscription’s status at the moment the request was rejected. The rejected request makes no change.
409 Conflict
A CANCELED or COMPLETED subscription is rejected earlier with 400 INVALID_STATE, which does not carry current_status. Validation problems in the block itself — a missing plan_id, an unsupported effective, a target plan in another currency, or a HONOR_PHASES ladder longer than the remaining billing cycles — return 400 BAD_REQUEST without current_status. The same applies to billing_cycle_behavior placement mistakes: RESTART without effective: IMMEDIATE, the top-level field combined with plan_change, or the top-level field on a plan-linked subscription each return 400 BAD_REQUEST with a message naming the fix. Where an invalid billing_cycle_behavior value is caught depends on its position: the top-level field is rejected at the API edge as 400 INVALID_PARAMETERS ("The field 'billing_cycle_behavior' has an invalid value."), while a bad value nested inside plan_change reaches the engine and returns 400 BAD_REQUEST (e.g. "RESET is invalid for plan_change.billing_cycle_behavior field") — treat both codes as validation failures. One exception to the plan-change error table above: PLAN_CHANGE_NOT_AVAILABLE applies to scheduled changes only. An IMMEDIATE change does not need an upcoming cycle, so it is accepted in the final billing cycle.

Setting a refund rule

refund_rule configures how a CUSTOM refund is sized when a later cancel request carries no refund_amount of its own. It is set here or on create, and sending "refund_rule": null clears it.
PERCENT and FIXED are exclusive — a rule carrying both percent and amount is rejected, and so is one carrying neither for its own type. A FIXED rule’s amount.currency must equal the subscription’s own currency; sending a mismatched one returns 400 REFUND_RULE_CURRENCY_MISMATCH — "refund_rule.amount.currency USD must match the subscription currency COP." (currencies vary with the request). The same check applies when the update changes the subscription’s own amount.currency while a FIXED rule in the old currency is still stored, without touching refund_rule in the same request — 400 REFUND_RULE_CURRENCY_MISMATCH — "stored refund rule is FIXED in COP; send refund_rule in USD or refund_rule: null in the same request." (naming the currencies involved). Resend the rule in the new currency, or clear it, in the same request that changes amount.currency. After a clear, refund_rule is omitted from the subscription object on the response — never sent back as null. See Cancel Subscription for how the rule is used, and the Subscription object for the field reference. Available to organizations enabled for it; contact Yuno — for an organization that is not enabled, sending refund_rule (including null) is silently ignored, and nothing changes.

Authorizations

public-api-key
string
header
required

Public API key from Dashboard > Settings > API Keys

private-secret-key
string
header
required

Private secret key (server-side only)

Path Parameters

subscription_id
string
required

The unique identifier of the subscription.

Body

application/json
name
string

The subscription plan name (MAX 255; MIN 3).

description
string

The subscription plan description (MAX 255; MIN 3).

account_id
string

The unique identifier of the account that will have the subscription plan available to use (MAX 64 ; MIN 36).

merchant_reference
string

Identification of the subscription plan (MAX 255; MIN 3).

soft_descriptor
string

Statement descriptor shown on the cardholder's bank statement. Updating it changes the descriptor applied to subsequent rebills generated by the subscription engine. Length and formatting limits vary by provider (for example, Unlimit truncates to 22 characters and Airwallex to 30). Worldpay does not read this field; it builds the statement narrative from payment_description instead.

Maximum string length: 255
country
string

The subscription's country.

amount
object

Specifies the amount object, with the value of each subscription payment and the used currency.

frequency
object

Specifies the frequency object. Defines the billing frequency for the subscription. Including type and value.

billing_cycles
object

Specifies the billing_cycles object. Defines the number of charges associated to the subscription.

customer_payer
object

Specifies the customer_payer object to identify the customer.

payment_method
object

Specifies the payment_method object. Supported types are CARD and PAYPAL_ENROLLMENT. When you send payment_method, type and vaulted_token are required, even if you only change card.usage.

availability
object

Specifies the availability object. Defines a date interval based on starting and ending dates when the subscription is available to use. If this object is omitted from the request, the availability dates are left unchanged. When it is included, omitting finish_at (or sending it as null) removes any previously set end date; omitting start_at leaves the current start date unchanged.

retries
object

The retries configuration to apply going forward. Accepts the same object as Create Subscription — including strategy and schedule — except cancel_on_exhausted_retries, which is create-only. Changes apply the next time retries are scheduled: a billing cycle already running its retries finishes on the configuration it started with, and the update takes effect from the next cycle.

plan_change
object | null

Moves the subscription to another plan. effective: NEXT_BILLING_CYCLE (the default) schedules the change for the next renewal — no proration, no mid-cycle charge. effective: IMMEDIATE applies the change in place, in this call: the subscription keeps its id, the new plan's price bills from the next charge, and billing_cycle_behavior decides what happens to the billing anchor. The subscription must be ACTIVE or TRIALING and already on a plan; the target plan must be ACTIVE. For scheduled changes only one can be pending at a time — send "plan_change": null to undo it before it applies (an IMMEDIATE change while another is pending returns 409). amount, frequency, billing_date and country are managed by the plan and cannot be sent for a plan-linked subscription — combining any of them with plan_change returns 400.

billing_cycle_behavior
enum<string>

For subscriptions that are not linked to a plan: re-anchors billing in the same way as plan_change.billing_cycle_behavior, typically combined with a frequency change in the same call (e.g. monthly to yearly, restarting the cycle from today). KEEP (default): the next charge date is untouched. RESTART: re-anchors the next charge to now plus the (possibly updated) frequency; the anchor alone resets — cycle counters, the retry configuration and the subscription status survive. On a subscription that has not started yet (CREATED — accepted here, unlike plan_change), the anchor clamps to its start date. Rules: cannot be combined with plan_change (400 — use the nested field); on a plan-linked subscription this field returns 400 (managed through plan_change); a PAUSED subscription returns 409 INVALID_SUBSCRIPTION_STATUS. Write-only: never echoed in any response.

Available options:
KEEP,
RESTART
metadata
object

Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to metadata.

refund_rule
object | null

Configures the refund rule used to resolve a later CUSTOM cancel refund when that cancel request sends no refund_amount of its own. Available to organizations enabled for it; contact Yuno. Send null to clear a previously configured rule.

Response

200

id
string
Example:

"0c7fed3e-ee0d-4d34-9547-778be4ec0798"

name
string
Example:

"Test Subscription"

account_id
string
Example:

"493e9374-510a-4201-9e09-de669d75f256"

country
string
Example:

"US"

description
string | null
Example:

"Subscription Test"

merchant_reference
string
Example:

"subscription-ref-merchant-AA01"

soft_descriptor
string | null

Soft descriptor.

Example:

"ACME SUBSCRIPTION"

status
string
Example:

"ACTIVE"

amount
object
frequency
object
billing_cycles
object
customer_payer
object
payment_method
object
availability
object
metadata
any
pending_plan_change
object | null

The plan change scheduled for the next billing cycle, or null when none is pending.

created_at
string
Example:

"2024-09-30T12:04:23.265372Z"

updated_at
string
Example:

"2024-09-30T12:04:23.265372Z"

current_period_start
string | null

Start of the billing period the subscription is currently in, one cadence before current_period_end. Inside a trial or any other plan phase it follows that phase's cadence. A terminal subscription (CANCELED or COMPLETED) keeps its final window rather than going null. Null only for a subscription with no recurring cadence. Always returned together with current_period_end.

Example:

"2023-01-16T20:00:00.912480Z"

current_period_end
string | null

End of the billing period the subscription is currently in. Equal to billing_cycles.next_at. Always returned together with current_period_start.

Example:

"2023-02-16T20:00:00.786342Z"

billing_date
object | null

Only present on subscriptions billed on a fixed date instead of a frequency; mutually exclusive with frequency.

retries
object | null

The retry policy applied when a recurring charge is declined. See Retries.

trial_period
object

Trial period.

additional_data
object | null

The optional order enrichment sent at creation, echoed back. See The Subscription Object for its full shape.

initial_payment_validation
boolean

Whether the subscription waited for its first payment to succeed before activating. false by default.

Example:

false

reason
string | null

Free-form reason for the subscription's current status, when one was recorded; null otherwise.

Example:

"merchant request"

renewal_notification_days
integer | null

How many days before the next billing date the subscription.close_to_renewal webhook is sent. null means no renewal notification is sent.

Example:

3

payments
string[]
subscription_agreement_id
string | null

Subscription agreement id.

Example:

"sa_6af2dfcd-44c0-4f16-a331-8bed3ed9c9fa"

cancellation_time
string | null

When the subscription was canceled. Only set once it reaches CANCELED; null otherwise.

Example:

"2023-12-16T20:46:54.786342Z"

cancellation_source
string | null

What triggered the cancellation: MERCHANT, SYSTEM, PLAN_CHANGE or RETRIES_EXHAUSTED. Only set once the subscription reaches CANCELED; null otherwise. Treat as open-ended — handle unrecognized values gracefully.

Example:

"MERCHANT"

ending_time
string | null

When the subscription completed or expired. Only set once it reaches COMPLETED; null otherwise.

Example:

"2024-12-16T20:46:54.786342Z"

plan_id
string

Only present when the subscription is linked to a plan.

Example:

"00000000-0000-4000-8000-000000000001"

plan_assigned_at
string

Only present on plan-linked subscriptions. The moment the plan was attached — at creation from a plan, migration onto a plan, or a plan change.

Example:

"2023-12-16T20:46:54.786342Z"

previous_subscription_id
string | null

Only present when this subscription was created by a plan change (switching an existing subscription to a different plan).

phases
object[]

The linked plan's phase definitions, as the plan declares them — including the terminal REGULAR phase. Only present on plan-linked subscriptions whose plan has phases. This is the plan's own ladder, not this subscriber's snapshot: for what was pinned for this subscription at creation, use billing_phases.

current_phase
string

Only present on plan-linked subscriptions.

Example:

"REGULAR"

current_phase_end
string | null

When the phase named by current_phase hands over to the next one — for a subscription in its trial, when it switches to the regular price. End of the last billing period the phase covers, so on the final period of a trial it equals current_period_end. Null for a subscription with no plan and for one that already left the phase ladder.

Example:

"2023-03-16T20:00:00.786342Z"

billing_phases
object[]

Only present on plan-linked subscriptions whose plan has phases. Contains the leading TRIAL phases only, snapshotted at creation — the terminal REGULAR phase is never included, since its price isn't pinned and instead resolves from the plan at every billing. current_phase: "REGULAR" is how you know the ladder is exhausted.

refund_rule
object

The stored refund rule used to resolve a CUSTOM cancel refund when the cancel request sends no refund_amount of its own. Present only when one is configured; omitted, not null, otherwise. Available to organizations enabled for it; contact Yuno.

pause_until
string

When the subscription is scheduled to resume automatically. Present only on a subscription currently paused with a duration (pause_cycles or pause_until sent at pause); absent, not null, on every other read.

Example:

"2026-10-15T15:54:22.780166Z"

cancel_scheduled
object | null

null when no cancellation is pending.

refund
object | null

null when refund was NONE or omitted. Present on the cancel response and on every later read once computed.

unpaid_cycle
object

Present only when the current billing cycle is PAST_DUE and has not settled.