Skip to content

Service products and free products

A service product is something you sell in the shop that is not a virtual machine or container, for example a dedicated server, a managed database or a support plan. The customer orders and pays it like a server; instead of creating a guest, NexoVirt records the order as a service and tells your system about it with a webhook. You (or your system, through the REST API) set it up and mark it as active.

Open Shop > Products > New product and choose Type: Service on the General tab. A service has no size, operating system, location or network, so the Resources, Location and network and Limits tabs disappear. Everything about selling stays: price, More billing periods, In the shop, the terms and withdrawal consent, customer groups and their discount, and Provisioning (automatic or by hand).

Service products cannot get add-ons (every add-on is a server resource) and cannot be changed to another plan.

The customer orders the service on the normal order page (no operating system or server name to choose), pays from the balance or by card or PayPal, and accepts your terms. Then:

  1. A service is created with the status Being set up (pending). The paid time does not start yet.
  2. The Service ordered webhook (service_ordered) is sent to your system, with the service, the customer (id, email, name, company) and the plan, period and amount.
  3. The customer gets the “We received your order” email and sees the service under My Services in the portal.

When the product (or the customer’s group, or the fraud check) uses manual provisioning, the order first waits in Shop > Orders. Accept and create the service there creates the service and sends the webhook; Reject and refund gives the money back.

Shop > Services lists every ordered service with its customer, reference, paid date and status. Open one and use Mark as active once it is set up:

  • Manage link: optional, https:// only. The customer opens it with the Manage button (in a new tab).
  • External reference: your system’s id, not shown to the customer.
  • Note to the customer: shown on the service page and in the “service is ready” email.

The paid time starts now. If it cannot be set up, Fail and refund puts the amount back on the customer’s balance (once) and cancels a card or PayPal subscription. On an active service you can edit these fields and use Suspend, Resume, Cancel now (ends at once, no refund) and Delete (removes it for good).

All endpoints need an admin token; tokens limited to hosts get 403. Every call is written to the audit log.

Method and pathWhat it does
GET /api/v1/servicesList (filters status, user_id, plan_id, external_ref; paginated)
GET /api/v1/services/{id}One service with customer and plan
POST /api/v1/services/{id}/activate{manage_url?, external_ref?, note?}: pending to active
POST /api/v1/services/{id}/fail{reason}: pending to failed, refunded once
PUT /api/v1/services/{id}Change manage_url, external_ref, note (only what you send)
POST /api/v1/services/{id}/suspendActive to suspended
POST /api/v1/services/{id}/resumeSuspended to active
POST /api/v1/services/{id}/confirm-usageUsage confirmation of a free service (see Usage confirmation)

Activate and fail answer 409 when the service is no longer pending; a manage link that is not https:// is 422.

Terminal window
curl -X POST https://panel.example.com/api/v1/services/42/activate \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"manage_url":"https://manage.example.com/ds-1-4711","external_ref":"ds-1-4711","note":"Your login is in a separate email."}'

Services renew exactly like servers: from the balance (with the low-balance and unpaid emails) or by an automatic card or PayPal payment. Because a service has nothing for NexoVirt to stop or delete, every step only changes the status and sends a webhook, and your system acts on it:

EventWebhook
A period was paidservice_renewed
Not paid at the paid date, or a cancelled service reached its endservice_suspended (status suspended)
Paid again after a suspension, or resumed by youservice_resumed (a resume that pays a period sends service_renewed first)
The customer cancelled, or you used Cancel nowservice_cancelled
The grace days after the suspension ended, or you used Deleteservice_deleted

After the grace days the service shows as Ended (status cancelled) and stays in the list as history.

Customers manage a service from My Services: they see its status, next renewal and price, open it with Manage, and use Turn auto-renew off, Renew now, Cancel subscription (ends at the paid date; can be withdrawn) and Stop automatic payment. Services also appear on the public Cancel contracts here page.

Every service event offers flat variables for your body template ({ServiceId}, {ServiceName}, {ServiceStatus}, {CustomerId}, {Email}, {Username}, {CustomerName}, {Company}, {PlanId}, {PlanName}, {PeriodDays}, {Amount}, {Currency}, {ExternalRef}, {ManageUrl}, {ExpiresAt}, {Reason}) plus {Payload}, the whole event as one JSON object. Use {Payload} without quotes:

{ "data": {Payload} }

{Payload} looks like this:

{
"event": "service_ordered",
"service": { "id": 42, "user_id": 7, "plan_id": 12, "name": "Dedicated server DS-1", "status": "pending",
"external_ref": null, "manage_url": null, "note": null, "price_cents": 4900, "auto_renew": true,
"expires_at": null, "activated_at": null, "created_at": "2026-10-30 12:00:00" },
"customer": { "id": 7, "email": "[email protected]", "username": "jdoe", "name": "Jane Doe", "company": "Example Ltd" },
"plan": { "id": 12, "name": "Dedicated server DS-1", "period_days": 30 },
"amount_cents": 4900,
"currency": "EUR",
"reason": ""
}

amount_cents is what was charged for the order (service_ordered) or for the period (service_renewed), else the price per period. reason explains suspensions, cancellations and deletions. Deliveries are signed and retried as described under Event webhooks.

Give a product (server or service, any billing period) the price 0.00 to make it free:

  • The order page shows Order for free: no payment step, no card or PayPal checkout, no balance check (a customer with a negative balance can still order). The name, operating system, terms and withdrawal consent, order confirmation and order email stay as usual. Add-ons are not offered at order time.
  • No invoice is created (invoices belong to payments, and nothing is paid). The order is kept in the customer’s balance history as a 0.00 booking.
  • A free product renews by itself every period, free of charge, until the customer cancels it. It is never suspended for money and no renewal, low-balance or unpaid emails are sent. A server that later gets paid extras renews like a paid one.
  • Free orders per customer (tab Price and periods, default 1, 0 = no limit) caps how many active free orders of this product (any of its billing periods) one customer may have. Orders waiting for manual setup count too. The limit is checked on the server when ordering.
  • Manual provisioning, the fraud check, customer groups and the consent record apply as for paid products. A customer discount never makes a paid product “free”.

Free products can ask the customer to confirm now and then that they still use them, so unused free servers and services do not stay around forever. On a product with the price 0.00 set:

  • Confirm usage every: the interval in days. Empty or 0 = Off (no confirmation, the default).
  • Grace period: days after a missed confirmation before the product is deleted (default 7).

How it runs for the customer:

  1. Three days before the due date (at the due date when the interval is shorter than 4 days) the customer gets the email Shop: usage confirmation due (free products) and a banner with the button Still using it on the portal dashboard, the server page and the service page. Confirming moves the due date one interval ahead.
  2. Not confirmed at the due date: the server or service is suspended and the customer gets Shop: suspended, usage not confirmed (free products). Confirming with Still using it resumes it (a server is started again).
  3. Not confirmed by the due date plus the grace period: it is deleted through the normal end path (the same deletion as after a cancellation).

Admins see the state as Shop: usage confirmation on the server’s Plan section and as Usage confirmation on the service page under Shop > Services, and can confirm for the customer with Confirm now (audit shop.usage_confirm). Paid products never ask for a confirmation.

Through the API, services and the guest detail carry usage_confirm_due_at (null when the product asks for no confirmation), and POST /api/v1/services/{id}/confirm-usage or POST /api/v1/hosts/{id}/guests/{type}/{vmid}/confirm-usage confirm on the customer’s behalf. Both answer {due_at, resumed}; 409 when the product is not running (for example still pending or ended), 422 when the product asks for no confirmation. The event webhooks usage_confirm_reminder and usage_confirm_suspended fire with the reminder and the suspension.

NexoVirt is developed and designed by Nucez Software, your partner for custom software and web design.