Skip to content

Blog ·

WISP billing automation

WISP enforces a suspension and has no idea whether anyone paid. The lifecycle mapping, the two-key trap, and the shapes that break a first integration.

By Provibr

WISP will suspend a server for you, and it will make that suspension stick: a customer who still has panel credentials cannot press start. What WISP does not have, and never claims to have, is any idea whether that customer paid you.

This page is about the layer in between. It also covers the one thing that makes WISP different from every other game panel here, which has nothing to do with its API: the panel is not yours.

The panel is hosted, the node is yours

WISP is sold as a hosted service. wisp.gg runs the panel; the node that carries your game servers runs on your own hardware. An agent inside your network talks outbound to the panel over its API and to your node's daemon for the console, so nothing has to be opened to the internet.

For a billing integration that split has one real consequence: there is nothing for you to upgrade and nothing for you to pin. WISP can change its API without a release you control. Provibr is tested against v3.7.5 and reads the running version from the panel itself, so drift turns up in the connection test rather than in a failed order at two in the morning.

One related detail worth knowing before you blame your own code. The panel's front door sits behind a Cloudflare challenge; every /api/… path goes straight through. That was measured with three different user agents and all three answered 200. It is a setting at wisp.gg, not at your end and not at ours, so if WISP ever puts the same challenge on the API, every WISP integration stops at once and there is nothing to fix locally. The failure has a recognisable shape (403, a Cf-Mitigated: challenge header, and HTML instead of JSON) and deserves its own message, so the panel says it will not let us in rather than wrong key.

The lifecycle, mapped

Billing eventWhat should happenWISP-side effect
Order paidBuild the server from the egg with the plan's limitsServer created, allocation assigned, the egg's installer runs
Invoice overdueSuspendsuspended set, and the panel holds it there
Payment receivedResumesuspended cleared
CancellationDeleteServer removed, allocation freed
ReinstallRun the egg's install script againcontainer.installed flips false, then true

Five actions, and all five answer with an empty 204. There is literally nothing in the body, so the house rule applies without exception: no mutation trusts its own answer. Read the server back afterwards.

Suspend is real, and it is idempotent

That combination is rarer than it should be among game panels, and it is the whole reason a dunning run on WISP is boring in a good way.

A second suspend on an already suspended server answers 204 again. Nothing to special-case, nothing to treat as "already that way", no error to swallow. Compare that with control panels where suspending twice is an authentication error, and you can see how much of a dunning integration is usually spent apologising for the panel.

Deleting works on a suspended server too, which matters for the cancellation that arrives a month after the suspension. A second delete answers 404, which is the correct outcome for a termination hook: there is nothing left to clean up.

The state rules on the Provibr side are stricter than the panel's, on purpose:

ActionAllowed from
startstopped, error, unknown
stop / restart / killrunning, error, unknown
suspendrunning, stopped, installing, error, unknown
resumesuspended
reinstallrunning, stopped, error

Read the suspend row twice. You can suspend a server that is already stopped, and one that is still installing. If suspension only worked on running servers, a customer could stop their own server, stop paying, and be untouchable. Enforcement you can dodge by pressing stop is not enforcement, and a cancellation that lands mid-install should not have to wait for the install to finish.

Two keys, no prefix, and the mistake that follows

WISP keeps Pterodactyl's split between two API surfaces, and no single key covers both. The application key builds, suspends, reinstalls and removes; the client key does power, live statistics and the console. Both are created on an administrator account.

Here is the trap. WISP keys carry no prefix, unlike Pterodactyl's ptla_ or Pelican's papp_: they are 48 characters with no recognisable head, so nothing can tell you that you pasted them the wrong way round. Not us, and not the panel either, because WISP answers a wrong key with 403 and an empty message. A 403 normally means this key is not allowed to do that, so a customer who swapped two keys goes into the panel and starts ticking permissions that were never the problem. If the connection test fails, swap the two keys before you change anything else.

The other half of that credential deserves a sentence in your own runbook: the application key is not only server management. The same node listing an integration reads also carries your node's licence key and daemon token. Treat it as a credential for the whole panel, and revoke it in WISP if you ever disconnect the integration.

Nothing is instant, and the API says so

Your panel lives in your own network and the agent dials outbound, so an endpoint that changes a machine cannot answer with the result. It answers that the order was accepted and hands you something to follow:

http
POST /v1/servers/{serverId}/actions/suspend
Authorization: Bearer pvk_...
Idempotency-Key: dunning-inv-10482
json
{
  "command": {
    "id": "cmd_...",
    "kind": "game.suspend",
    "status": "pending"
  }
}

That is 202 Accepted, not 200. You then read the command until its status is final: done, failed, timed_out or cancelled. Reading never works this way, because an inventory or an invoice comes out of a database rather than off your host.

Anything that creates something accepts an Idempotency-Key of your own making. Key it on something stable from your side, such as the invoice id plus the dunning stage, and a fifteen-minute cron, a retried webhook and an admin who clicks twice all become harmless.

Two shapes that broke this integration before you met it

Both are worth reading even if you never touch WISP, because both are the kind of failure that reports success.

Every identity is a prefixed string, not a number. A WISP server is server_01m0n8m94afcj2kk5mzt9z379q; nodes, eggs and allocations look the same. The platform had a check that an external game server id was letters and digits, which is true of every other game panel and false here. The result was not an error message: over-limit WISP customers were reported to the enforcement worker and then skipped, every single round, forever. They were flagged and never suspended. One shared identity check now covers both shapes.

A disk weight of zero is refused. WISP requires limits.io and demands a value between 10 and 1000. The platform's plan field defaults to zero, so every WISP order would have ended in a 422 about a field most people have never heard of. There are two fences now: the plan form refuses a zero and says why, and the agent lifts a zero to the default weight before sending. Two fences for one mistake is deliberate, because one of them is on the side you can see.

What you cannot see, and should not pretend to

This is the honest part, and it is short.

  • There is no status field on a WISP server. Pterodactyl has one, with installing and install_failed in it. WISP has suspended and container.installed, both booleans. A failed installation therefore looks exactly like a running one: installed stays false in both cases. Being built and broken are the same picture on this surface, and a product that claims otherwise is guessing.
  • A node does not report its own occupancy. The field other panels use for that is absent, including when you ask for it explicitly. Occupancy is summed from the servers instead, which works and is one call more.
  • There is no uptime in the live statistics. A container that restarted between two polls cannot be recognised with certainty. Set against that, WISP does not cache those statistics for twenty seconds the way Pterodactyl does, so the graphs do not invent a dip that never happened. Net better, with one sharp edge.

Capacity, and how it meets dunning

Provibr bills you for capacity, not for your customers. What you charge for a game server stays entirely inside your own billing system; we never see a customer invoice and never talk to your customers.

The rule that catches everyone in month one: every server counts, running or not. A suspended one does not, and neither does a deleted one. Stopping a machine frees nothing.

That lines up with dunning rather neatly. Suspend a customer for non-payment and they stop counting against your capacity that moment; resume them when they pay and they count again. You are not paying platform capacity for someone who is not paying you.

What is deliberately not automated

Provibr manages servers, not the panel. It does not install WISP, does not touch your node's daemon beyond the console, and does not manage backups, firewall rules or IP assignment on this provider: those capabilities are off in the register rather than half implemented. There is no metered billing on WISP usage, because per-server statistics are shown in the panel and not exposed over the API, and an endpoint that does not exist cannot be billed against.

If you are still choosing, the comparison worth reading first is choosing a game panel by the API you automate, which measures six panels on exactly one question: whether a suspension does anything. The connection itself, the two keys and the fingerprint field are covered on the WISP integration page.

Frequently asked questions

Does WISP have billing of its own?

No. WISP hosts and controls game servers; it has no orders, no invoices, no dunning and no idea which customer is behind which server. The commercial layer is one you add.

Does suspending a server in WISP actually stop the customer?

Yes. WISP has a real suspend primitive on its application API, and a customer with panel credentials cannot start a suspended server. That is not true of every game panel: PufferPanel has no suspend at all, and GameAP has a flag that was measured to change nothing.

Is it safe for a dunning run to suspend the same server twice?

Yes. A second suspend answers 204 exactly like the first. There is no "already suspended" error to catch and no state to check first, which is what makes a nightly dunning job on WISP simple.

Why do the two WISP API keys get mixed up so often?

Because they carry no prefix. Pterodactyl and Pelican label their keys; WISP's are 48 characters with no recognisable head. A swapped pair produces a 403 with an empty message, which reads as a permissions problem rather than the paste error it is.

Can I bill WISP customers for what they used?

Not through the API. Per-server usage is visible in the panel but is not exposed as an endpoint, so metered billing has nothing to read. Capacity-based pricing per server is what works today.

Start with one host and one agent

Create an account, install the agent with a single command and connect your first Proxmox host. The trial runs for 14 days and asks for no payment details.