Hosting plans & billing periods
Hosting plans & billing periods — quick guide for hosting providers
A hosting plan says what an account may use (websites, disk, databases…). A plan period says until when the account is paid for. VinzaPanel warns before the end date, keeps the account running for a few grace days, then suspends it — and brings it back as soon as you renew. Nothing is ever deleted.
Requires VinzaPanel 1.3.71 or newer.
Concepts
| Term | Meaning |
|---|---|
| Start date | First day of the current period. |
| End date | Last paid day. On this day the account still works. Empty = no expiry. |
| Billing cycle | Monthly, quarterly, every 6 months, yearly, custom (N days) or no expiry. Renewing adds whole cycles. |
| Warn days before | How many days before the end date the admins and the customer get a notice (panel bell + email). |
| Grace days | Days after the end date during which the account keeps working. |
| At expiry | Warn, then suspend after the grace days (default) or Warn only. |
| Auto-renew | The panel extends the end date by one cycle by itself when it passes — for accounts you bill outside a billing system. Leave it off when WHMCS, ClientExec or Blesta renews through the API. |
The status badge shows where an account is:
| Status | When |
|---|---|
| Active | More than warn days before the end date, or no end date. |
| Expiring | Within warn days of the end date. |
| Grace | Past the end date, still within the grace days. |
| Expired | Past the end date and the grace days. With suspend the account is suspended now. |
A suspension caused by the expiry is lifted automatically when you renew. An account you suspended yourself for another reason (abuse, for example) stays suspended.
Set it up in 5 steps
- Create your plans — Administration → Hosting Plans (
/plans/). See Hosting Plans. - Open the account — Server → Users, click the account, then Edit.
- Set a period — in the Plan period card pick the start date and billing cycle. Leave End date empty to end after one cycle (monthly from 1 Jan → 31 Jan), or type an end date.
- Choose grace and warning — e.g. warn 7 days before, 7 grace days, Warn, then suspend. Click Save period and confirm.
- Check the list — the Users list shows each account's end date with an Active / Expiring / Grace / Expired tag. The background job plan_expiry (Scheduler → Background Worker Jobs) checks every account hourly; each notice is sent once per period.
Renewing
- Renew button (edit page) — adds 1 or more cycles to the end date. Monthly periods keep their day of month (a period that started on the 31st ends on the 30th/31st, never drifts to the 28th).
- Count from today — for a period that lapsed long ago: the new cycles start today instead of at the old end date.
- Change dates & settings — type any start and end date. Remove end date makes the account never expire.
Each change is kept in the card's History (who, when, old → new end date).
API (billing systems)
All calls use a token with the Provisioning scope (Profile → API tokens) and the same rules as the Provisioning API: Authorization: Bearer …, JSON bodies, optional Idempotency-Key header on POSTs.
PANEL=https://panel.example.com:2711/api/v1/provisioning
TOKEN=vzp_xxxxxxxxxxxxxxxx
Assign a plan with a yearly period
curl -s -X POST "$PANEL/accounts/alice/period/" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-H "Idempotency-Key: order-1001" \
-d '{"plan": "Business", "start_date": "2026-11-01", "cycle": "yearly",
"grace_days": 7, "notify_days": 14, "on_expiry": "suspend"}'
{"success": true, "username": "alice", "status": "active",
"plan": {"id": "3", "name": "Business"},
"period": {"start_date": "2026-11-01", "end_date": "2027-10-31", "cycle": "yearly",
"cycle_days": 30, "auto_renew": false, "grace_days": 7, "notify_days": 14,
"on_expiry": "suspend", "status": "active", "days_left": 360,
"grace_ends": "2027-11-07", "suspended_for_expiry": false,
"history": [{"action": "assign", "actor": "billing (api:whmcs)", "old_end": null,
"new_end": "2027-10-31", "at": "2026-11-01T08:00:00+00:00", "...": "..."}]}}
Every field is optional; fields you leave out keep their value. "cycle": "none" removes the end date, "plan": null removes the plan.
Renew after payment — one more cycle, or an exact end date:
curl -s -X POST "$PANEL/accounts/alice/period/renew/" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-H "Idempotency-Key: invoice-5521" \
-d '{"cycles": 1}'
curl -s -X POST "$PANEL/accounts/alice/period/renew/" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"end_date": "2028-12-31"}'
Add "from_today": true to restart a long-lapsed period today. The answer has the same shape as above; a suspension caused by the expiry is lifted in the same call.
Read an account's period
curl -s "$PANEL/accounts/alice/period/" -H "Authorization: Bearer $TOKEN"
GET /accounts/alice/ also includes it as plan_period (null = no period).
List accounts that expire soon (default 14 days, expired ones included):
curl -s "$PANEL/expiring/?days=30&include_expired=false" -H "Authorization: Bearer $TOKEN"
{"success": true, "days": 30, "total": 1,
"accounts": [{"username": "alice", "email": "[email protected]", "plan": "Business",
"suspended": false,
"period": {"end_date": "2026-11-20", "status": "expiring", "days_left": 12, "...": "..."}}]}
Errors answer 400 with {"success": false, "error": "…"} (bad date, unknown cycle or plan, renewing an account without a period), 403 for admin accounts or a token without user-management rights, 404 for an unknown username.