Reseller API
Issue Xenoware subscriptions to your customers programmatically. Your storefront, your payments, your prices. Same launcher every retail customer uses.
Getting startedOverview
Resell Xenoware. The Reseller API lets you issue subscriptions to your customers, manage their lifecycle, and receive webhooks on state changes. Your customers use the standard Xenoware launcher with their own xenoware.xyz account - your brand lives on your storefront, not on the launcher itself.
- Flat 50% wholesale discount off retail for every approved reseller. No volume tiers, no minimum.
- You take payment from your customer in your own currency, through your own payment processor - you keep the customer relationship and the margin above wholesale.
- Your backend calls
POST /api/reseller/licensesto issue a subscription. We bill your wholesale balance and either grant the existing account or email the customer a link to set up theirs. - We invoice you weekly for the wholesale cost of all licenses issued in the period (net-7 default).
Getting startedAuthentication
Base URL: https://xenoware.xyz/api/reseller
All endpoints require a bearer token in the Authorizationheader. Your reseller API key is the bearer token - no separate token exchange.
Authorization: Bearer rsk_live_d8e2c5...Keys come in two flavours: rsk_test_ (sandbox environment, no real money) and rsk_live_ (production, billed). Use sandbox until your integration is clean end-to-end.
Getting startedErrors
All errors follow this shape:
{ "error": "code", "detail": "optional human description" }Common codes:
401 invalid_key- Authorization header missing or key not recognised.403 account_suspended- your reseller account has been frozen (typically for non-payment).403 insufficient_credit- outstanding balance exceeds your credit limit; pay your invoice to re-enable issuance.400 invalid_request- Zod validation failed. Thedetailfield includes which field was bad.409 customer_exists- the email or username is already in use by an existing Xenoware account. Reuse the existing license via extend.429 rate_limited- slow down; checkRetry-Afterheader.5xx server_error- transient; retry with backoff. Idempotency viaexternalIdmeans retry is always safe.
Getting startedRate limits
- 120 issuances per minute per reseller key. Email resellers@xenoware.xyz ahead of a flash sale if you expect a spike.
- 600 reads per minute - list + detail combined.
- 60 webhook deliveries per minute, per URL - we retry failed deliveries with exponential backoff (1m, 5m, 30m, 2h, 12h) for up to 24h.
LicensesIssue a license
Creates a Xenoware end-user account and an active subscription on the specified game + plan. Idempotent on externalId: re-posting the same external id returns the existing license rather than double-billing.
POST /api/reseller/licenses
Authorization: Bearer rsk_live_…
Content-Type: application/json
{
"externalId": "order_19238",
"customerEmail": "buyer@example.com",
"customerUsername": "buyer123",
"game": "eft",
"plan": "1m",
"notify": true
}Field reference:
externalId(string, required, ≤64 chars) - your internal order id. Used for idempotency.customerEmail(string, required) - must be a real email; we send a one-time link the customer clicks to set their own password.customerUsername(string, optional, 3–30 chars,[A-Za-z0-9_]) - claimed at creation. If omitted, the customer picks one at first sign-in.game(string, required) - currently only"eft"; new games announced as they ship.plan(string, required) - one of"1d","1w","1m","3m".notify(boolean, defaulttrue) - if true, we email the customer the activation link; if false, you handle delivery yourself.
Response (200):
{
"licenseId": "lic_2d8f3a...",
"subscriptionId": "sub_91b7c2...",
"customerEmail": "buyer@example.com",
"activationUrl": "https://xenoware.xyz/activate?t=...",
"expiresAt": "2026-06-14T13:42:01.000Z",
"wholesaleCents": 3149,
"currency": "EUR"
}LicensesList licenses
Newest first. Paginated.
GET /api/reseller/licenses?cursor=…&limit=50&game=eft&status=active
Authorization: Bearer rsk_live_…limit- default 50, max 200.cursor- opaque cursor from a previous response'snextCursor.game- filter to a single game.status- one of"active","expired","revoked","refunded".
LicensesGet a license
GET /api/reseller/licenses/lic_2d8f3a...Returns the same row shape as the list endpoint.
LicensesExtend a license
Adds time to an existing license. Pricing follows the same wholesale rate as a new issuance - flat 50% off retail.
POST /api/reseller/licenses/lic_2d8f3a.../extend
Content-Type: application/json
{ "plan": "1m", "externalId": "order_19238_renew" }LicensesRevoke a license
Revokes the license immediately. The customer's loader stops working within 60 seconds. Use this for fraud, chargebacks, or support resolutions on your end.
POST /api/reseller/licenses/lic_2d8f3a.../revoke
Content-Type: application/json
{ "reason": "chargeback on order 19238" }Revocation does not automatically refund the wholesale charge - see Billing & refunds.
AccountAccount info
Returns your current outstanding balance, last invoice date, and the wholesale price table.
GET /api/reseller/account
Authorization: Bearer rsk_live_…{
"resellerId": "rsl_a1b2c3...",
"name": "Acme Cheats Ltd.",
"discountPercent": 50,
"outstandingCents": 12450,
"currency": "EUR",
"lastInvoiceAt": "2026-05-07T00:00:00.000Z",
"nextInvoiceAt": "2026-05-14T00:00:00.000Z",
"prices": {
"eft": {
"1d": { "retailCents": 499, "wholesaleCents": 250 },
"1w": { "retailCents": 2199, "wholesaleCents": 1100 },
"1m": { "retailCents": 4499, "wholesaleCents": 2250 },
"3m": { "retailCents": 8499, "wholesaleCents": 4250 }
}
}
}AccountBilling & refunds
- Invoices are generated weekly (Mondays, 09:00 UTC) and settled net-7 by default.
- Wholesale charges are not refunded automatically on customer-side revoke. To request a credit, contact us within 24 hours of issuance, and only if the license was never activated (no loader sign-in, no engine boot).
- Licenses banned for TOS violation (including AssetPay trade reversal) are not creditable - the charge stands.
WebhooksConfiguring
Set your webhook URL in the reseller dashboard. We POST JSON payloads when meaningful state transitions happen to licenses you've issued. All payloads are HMAC-SHA256 signed.
WebhooksEvent types
license.activated- customer completed first sign-in.license.expired- subscription ran out.license.revoked- customer revoked via /revoke.license.banned- customer was system-banned (TOS violation, AssetPay reversal, etc.). The wholesale charge is not refunded on a ban - bans only happen for customer misconduct.license.hwid_changed- admin reset the HWID at our discretion (rare).
WebhooksSignature verification
Each delivery carries an X-Reseller-Signature header with the HMAC-SHA256 hex digest of the raw request body, signed with your webhook signing secret. Compare in constant time:
import { createHmac, timingSafeEqual } from "crypto";
function verify(rawBody, signatureHeader, secret) {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(signatureHeader ?? "", "hex");
return a.length === b.length && timingSafeEqual(a, b);
}Example payload:
POST https://your-app.example/xenoware-webhook
X-Reseller-Signature: 7c3a9e1d...
Content-Type: application/json
{
"event": "license.activated",
"timestamp": "2026-05-14T10:12:33.123Z",
"licenseId": "lic_2d8f3a...",
"externalId": "order_19238",
"customerEmail": "buyer@example.com",
"game": "eft",
"plan": "1m",
"expiresAt": "2026-06-14T13:42:01.000Z"
}OtherSandbox
Use rsk_test_keys against the same base URL. Sandbox issuances don't accrue real wholesale charges and don't fire production webhooks (a sandbox: true field is added to test events so your handler can no-op on them). Test licenses auto-revoke after 24 hours regardless of plan.
OtherCustomer experience
Customers use the standard Xenoware launcher and dashboard at xenoware.xyz with their own account. Your branding lives where you sell - your storefront, your checkout, your support channels - and we deliver the technical product underneath. The launcher itself is not co-branded.
When you issue a license to a new email, the customer receives an activation email naming your company ("<your name> bought you a subscription, click here to set up your account"). When you issue to an email that already has a Xenoware account, the subscription is added immediately and the customer gets a one-line notice about who added it.
Subscription mechanics (license keys, HWID binding, 2FA, refund flow) are identical to retail. Customers churning between resellers are tied to a single Xenoware account by email - contact us if you need a fresh-account workflow.
OtherReference client
A minimal Node.js reseller client is published at github.com/xxxenomus/xenoware-reseller-js (sample only - not officially supported). It demonstrates idempotent issuance, webhook signature verification, and retry with backoff in ~100 lines.
