Skip to main content
Purchase verification lets a buyer prove they own a product to anyone, a Discord bot, a support desk, a seller’s own site, using a disposable code instead of their account details.
Tier: Free, no studio key requried.

How the flow works

  1. A logged-in buyer generates a code for a product they own. It looks like HFP-QCN-VJ7.
  2. The buyer pastes the code wherever verification is needed (e.g. a /verify Discord command).
  3. Your integration calls the public check endpoint with the code.
  4. You get back a verdict plus the product it proves ownership of, and nothing about the buyer.
Codes are:
  • Short-lived, valid for 10 minutes.
  • Single-use, the first successful check consumes the code; anyone replaying it gets already_used. Only one checker can ever receive valid: true.
  • Privacy-preserving, the check response never contains the buyer’s identity, so no user data changes hands.

Generating a code (buyer side)

The easiest way: send buyers to the verification page on the store,
https://nmcrate.com/purchase/verify/{productId}
It shows the product, the code in large type with a copy button, and a live expiry countdown. Programmatic alternative: POST /api/purchases/verify/{productId} with a logged-in NMCrate session. productId accepts the product slug or id.
{
  "token": "HFP-QCN-VJ7",
  "expiresAt": "2026-07-13T19:44:12.107Z",
  "product": {
    "id": "craftengine_expansion",
    "slug": "craftengine-support",
    "name": "CraftEngine Support"
  }
}
Requesting again while a code is still fresh returns the same code rather than minting a new one. If the account doesn’t own the product, the response is 403 with code: "NOT_OWNED".

Checking a code (integrator side)

GET /api/purchases/verify/check/{token}, public, no authentication; the code itself is the proof.
curl https://nmcrate.com/api/purchases/verify/check/HFP-QCN-VJ7
{
  "valid": true,
  "product": {
    "id": "craftengine_expansion",
    "slug": "craftengine-support",
    "name": "CraftEngine Support"
  },
  "generatedAt": "2026-07-13T19:34:12.134Z"
}
Input is forgiving: lowercase, missing dashes, and stray whitespace are normalized (hfpqcnvj7 works). The response is always HTTP 200, the verdict is in the body:
validreasonMeaning
true,The code proves ownership of product. It is now consumed.
falseinvalid_formatThe input can’t be a code (must be 9 characters, A–Z / 2–9).
falsenot_foundNo such code, mistyped, or expired long enough ago to be purged.
falseexpiredThe code existed but its 10 minutes are up.
falsealready_usedThe code was already checked once. Treat with suspicion, someone may be replaying a code they saw elsewhere.
Always compare the returned product.id (or slug) against the product you are verifying for. A valid code proves ownership of the product it was generated for, not of your product specifically.

Example: Discord bot verification

User:  /verify HFP-QCN-VJ7
Bot →  GET /api/purchases/verify/check/HFP-QCN-VJ7
Bot:   checks valid === true && product.slug === "craftengine-support"
Bot:   assigns the "Customer" role
Because codes are single-use and expire in 10 minutes, a code screenshotted or pasted in a public channel can’t be reused by someone else after your bot has checked it.