Before you start
- API in TEST:
https://test-api.segmnt.cloud - Every billing call on this page uses your session cookie (the dashboard session). An API key cannot call them.
- Prices are in SAR per month, before VAT.
- The plans on sale are Starter, Pro and Agency. The free plan is the default; you do not buy it.
1. The plans on sale
GET /v1/billing/plans returns the plans a customer can buy.
Notes:
price.amountis in halalas (1 SAR = 100 halalas).5900halalas is 59 SAR. This is the same unit the API uses everywhere.price.currencyisSARandprice.intervalismonth.priceis before VAT. See VAT below.limitsare the plan limits (stored hours, new uploads, watched minutes, domains, projects, quality and the logo). See Usage and plan limits for what they mean.- The prices and limits come from the plan catalog. They are the same numbers you see on the site.
- This call needs the session cookie. Without it you get
401 unauthorized.
2. Start a checkout
POST /v1/billing/checkout returns the payment provider’s hosted page. Send the
customer there. The customer pays on Moyasar with mada, Apple Pay, STC Pay
or a card.
201):
- The plan does not change here. It changes only after the provider confirms the payment (section 3).
checkoutUrlis the provider’s hosted page. Redirect the user to it.expiresAtisnullwhen the provider sets no deadline. Moyasar invoices do not expire by default, so do not invent one.planIdmust be one of the plans on sale. An unknown or free plan gets400 invalid_request.discountCodeis optional. Send an issued code and it is applied before the provider is called. See Discount codes below.- If online payment is not configured, checkout gets
503 billing_unavailable. Use a manual invoice. - If the provider does not answer, checkout gets
502 billing_provider_error. Try again.
Discount codes
POST /v1/billing/checkout takes an optional discountCode. A code is applied
when it has been issued to you; creating a code is an internal segmnt action,
so this page does not show how to create one.
- A founding code gives 30% off and a referral code gives 10% off. The total discount is capped at 40%, so no code can take off more than that.
- Codes are case-insensitive and trimmed, so
founding-30andFOUNDING-30are the same code. - The code is checked before the payment provider is called. An unknown,
expired or used-up code answers
400 invalid_requestwith a clear message, so the customer is never charged the full price by accident. - The discount is applied to the plan price before VAT: VAT (15%) is charged on the discounted net, not on the full price.
- The 15% VAT invoice shows the plan line and, when a code was applied, a second negative discount line. The lines still add up to the invoice total.
- The code is counted only when a payment is confirmed. A provider retry does not count the same code twice.
- A confirmed payment stores the code on the subscription (
discount_code). - The discount is kept on every renewal for the promised number of months.
A founding code or a referral for the referrer lasts 12 months; a referral
for the new customer lasts 3 months. The first discounted payment locks the
promise on the subscription, and each later hosted payment re-applies it. Send
no
discountCodeon a renewal: the promise stored on the subscription is applied automatically. Once the window is used up, the renewal is at the full price again. - The code’s
usedcounter counts a customer once, on the first discounted payment, not once per renewal. Somax_uses = 10means 10 customers, even though each of them may pay 12 discounted months. - A locked promise is honored even when the code later expires or reaches its
cap: the customer was promised the discount. A new customer using that same
expired or used-up code is still refused with
400 invalid_request. - The 40% cap applies on every payment, including a renewal.
3. The payment that changes the plan
When the customer pays, Moyasar callsPOST /v1/billing/providers/moyasar/webhook. This route is for the provider,
not for customers. It has no segmnt session; it is checked with the provider’s
own secret token.
What happens on every verified event:
- The provider’s secret token is checked with a constant-time compare.
- The webhook body is never trusted. We read the merchant invoice back from the provider API.
- The amount and currency are checked against the plan’s fixed price (plus 15% VAT when VAT mode is on).
- The payment, the 15% VAT invoice, the subscription and the account plan are written in one batch, so the event is applied once and cannot be applied half way.
204 and does not change the plan again or
extend the period.
A refund reverses an applied payment. When the provider reports a refunded
payment, the payment is recorded as refunded, the subscription is set to
canceled, and the account returns to the free plan. The invoice row is
kept: a refund is not a delete, and the fiscal sequence must stay gapless
(a ZATCA credit note is a later open point). A refund is idempotent too, so
a retried refund changes nothing.
Answers:
A verified event that does not match is refused with
400 and changes no plan.
This covers a wrong amount, a currency that is not SAR, a missing plan or
account in the invoice, an unknown plan, and an account we do not have.
4. Check the plan after payment
After a confirmed payment,GET /v1/billing/plan returns the paid plan, an
active subscription period, the new limits and your usage. The call is
documented on the Usage and plan limits page.
limitsis the paid plan’s limits (stored hours, watched minutes, domains, quality and the logo). Inlimits,nullmeans no limit.usageis your account now: stored seconds, watched minutes in the current period, and verified domains.- The new-upload and project limits are enforced, but they are not in this answer yet. See Usage and plan limits for the full limits table.
VAT
VAT is not on yet. Today VAT is off:- When VAT is off, the customer pays the plan price and the invoice shows
0VAT. - When VAT mode is on, the amount is the plan price plus 15%, and the invoice records the VAT.
unitAmount (net),
vatAmount and amount (gross, net + VAT). So the lines add up to
invoices.total when VAT mode is on.
Not live yet
- The annual plan. Only the monthly plan is on sale.
- The invoice PDF. The 15% VAT invoice is written, but its PDF file is not built yet.
Errors
Errors useapplication/problem+json. Use the code field in your code, not
the text. The type field links to https://docs.segmnt.cloud/errors/{code}.
Tested in TEST
Checked againsthttps://test-api.segmnt.cloud on 14 Oct 2026:
GET /v1/billing/planswith no session ->401 unauthorized.POST /v1/billing/checkoutwith no session ->401 unauthorized.POST /v1/billing/providers/moyasar/webhookwith no webhook secret configured ->503 billing_unavailable.POST /v1/billing/checkoutwith an unknown plan ->400 invalid_request(unit test).POST /v1/billing/checkoutwith an unknown, expired or used-updiscountCode->400 invalid_request(unit test).
400 refusal path) is
proved by the API tests in api/src/billing.test.ts. The same tests cover the
discount paths: a founding code (30%), a referral code (10%), the 40% cap, the
plan line plus the negative discount line that still reconcile to the total, and
a replay that does not count the code twice. The live Moyasar sandbox run needs
the payment provider’s sandbox keys, so the end-to-end provider call is not run
on TEST yet.