What billing cycles can I configure?
periodType and periodInterval together define the cycle length. Both are
required, and the accepted periodInterval range depends on periodType:
There is no
YEARLY period type. For an annual subscription use
periodType: MONTHLY with periodInterval: 12.
The same two fields cover the cycles merchants usually ask about by name:
Individual payment methods may narrow this further — some reject daily cycles,
and some accept only certain cycle/interval combinations. A cycle that passes
the ranges above can still be refused for the payment method you selected.
Can the trial period use a different cycle from the regular period?
Yes. The trial cycle is configured independently of the regular cycle throughtrialPeriodType and trialPeriodInterval, and the accepted ranges are the
same as for the regular period:
trialPeriodTypeandtrialPeriodIntervalmust be supplied together — one without the other is rejected.- If you omit both, the trial simply reuses
periodTypeandperiodInterval. - Independent configuration does not mean no cross-checks. See the next question.
Which subscription fields have to be supplied together?
And these cross-field rules are enforced even though the trial cycle is
configured independently:
How many periods can a subscription run for?
“No merchant-defined end” is not literally unlimited: a subscription with no
numberOfPeriod is still bounded by the maximum duration on your subscription
agreement, and Waffo derives an effective final period count from that duration
and your cycle. Some payment methods impose their own maximum duration too. So
plan for a subscription that eventually reaches a final period even when you
never set one.
Once a subscription has reached its final period, amount updates are rejected —
see the amount page.
Are goodsUrl and appName required when I create a subscription?
No. On /api/v1/subscription/create both are optional, and you can create a
subscription with neither.
The “provide either goodsUrl or appName” rule you may have seen belongs to
one-off order creation, not to subscription creation. Individual agreements
can still require goodsUrl for compliance and risk reasons, so supplying one
of the two remains good practice — but it is not a blanket subscription-create
validation, and building your integration around it as if it were will mislead
you when you debug a rejection.
What are all the subscription statuses, and which transitions are legal?
Nine statuses exist. Each one can only be reached from a specific set of source statuses, so a status you did not expect is usually a sign you missed an earlier transition rather than a sign of an invalid state:
Grouping them the way your integration probably needs to:
Treat the whole terminal group as “no further billing period will open” and
switch on the group, not on individual values — that way a status you have not
mapped cannot silently look like an active subscription.
What is PLATFORM_CANCELLED?
It is the terminal status Waffo sets when it cancels a subscription on its own
initiative because one of that subscription’s charges produced a dispute,
chargeback or fraud signal. It reaches you through the subscription status
notification like any other cancellation, and the notification carries a cancel
reason identifying which kind of signal triggered it.
Points that matter for your integration:
- It is only reachable from
ACTIVE. - Automatic cancellation is configurable per merchant and per signal type, and is off unless configured — so you may never see this value, and its absence does not mean disputes are being ignored.
- Because Waffo, not you, initiated it, your own records will show the subscription as active until you process the notification. Treat the webhook as the source of truth.
Creation returned AUTHORIZATION_REQUIRED. What do I do?
The user still has to authorize the recurring mandate. The creation response
carries subscriptionAction, which contains the URL to send the user to
(webUrl). Redirect the user there; the subscription moves to ACTIVE once
authorization succeeds.
subscriptionAction is only populated while the subscription is in
IN_PROGRESS or AUTHORIZATION_REQUIRED. Once the subscription is active or
terminal it is no longer returned, which is expected rather than an error.
The authorization link is time-limited. orderExpiredAt on the create request
sets that deadline; if you omit it, a platform default applies, and it is
short — minutes rather than hours — so send the user to the link straight
away. A subscription whose authorization is never completed inside the window
moves to CLOSE.
What happens if I call create again with the same request ID?
Creation is idempotent onsubscriptionRequest — the request ID you
generate — and not on merchantSubscriptionId. A repeat call with the same
subscriptionRequest under the same MID does not create a second subscription.
What comes back depends on where the existing subscription already got to:
A repeat call is also validated for consistency: if the
amount or currency
differ from the original request, the call is rejected as a duplicate rather
than silently accepted. So retry with the identical payload, and do not reuse
one subscriptionRequest for two different subscriptions.
If a user abandons authorization and you need to give them a fresh link, retry
creation while the subscription is still pre-active and use whatever
subscriptionAction comes back; do not cache a link and assume it stays valid.
How do I query a subscription, and how do I get the per-period charges?
Call/api/v1/subscription/inquiry with subscriptionRequest or
subscriptionId — at least one is required. Passing both is accepted.
Per-period charges are not returned by default. Set paymentDetails to 1
to get them (0, the default, omits them; any other value is rejected):
Each
paymentDetails entry carries acquiringOrderId, orderCurrency,
orderAmount, orderStatus, orderUpdatedAt and period, plus a
topupDetails list on periods that had additional (seat add-on) charges — see
Cross-Currency Subscriptions & Portal FAQ
for how those rows are numbered.
Going the other way — from a charge to its subscription — the order inquiry
response carries a subscriptionInfo block with subscriptionId, period,
subscriptionRequest and a subscriptionEvent telling you whether the charge
was a period charge or a payment-method addition. One-off orders have no
subscriptionInfo.
The subscription response also reports productInfo.currentPeriod and the
stored productInfo.scheduledAmounts, which is what you read before scheduling
an amount change.
How many subscriptions can one user have?
Any number. Under one MID, the samemerchantUserId can hold as many
subscriptions as you create — there is no per-user cap and no uniqueness check
on merchantUserId at creation.
This is the normal shape for multi-product and multi-seat businesses: one user,
several concurrent subscriptions, each with its own subscriptionId and its own
lifecycle. Because of that, never treat merchantUserId as a key for “the
user’s subscription” — key on subscriptionId or on your own
merchantSubscriptionId.
Can a subscription start on a future date?
Yes — passstartTime on create. Validation:
Backdating is not supported. There is no way to create a subscription that
behaves as though it had started earlier, and no way to make the first period
land in the past. If you need to bill for a period that has already elapsed,
collect it as a separate one-off order.
Two further constraints when you defer a start:
- The start date must fall inside the validity window of the agreement that will be used, so a start far out can be rejected on those grounds even within 365 days.
- On a plan change,
startTimemust not be earlier than the original subscription’s next scheduled charge.