30% off·ends in 4d 3h 13m
Xenoware
← Home

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/licenses to 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. The detail field 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; check Retry-After header.
  • 5xx server_error - transient; retry with backoff. Idempotency via externalId means 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, default true) - 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's nextCursor.
  • 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.