Documentation

Everything you need to obfuscate Luau: the HTTP API and the in-script macros.

Overview

The MoonVeil API obfuscates, prettifies, and minifies Luau over HTTP. The base URL is https://moonveil.cc/api, and every endpoint is versioned under /v2.

A successful response returns the transformed script as text/plain. Errors return JSON shaped like { "error": "..." }.

Authentication

Send your API key as a bearer token on every request. The endpoint examples below reuse this client:

const KEY = process.env.MOONVEIL_KEY!;

async function moonveil(op: string, body: object) {
  const res = await fetch(`https://moonveil.cc/api/v2/${op}`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error((await res.json()).error);
  return res.text();
}

Browser sessions authenticate with the token cookie instead, which the dashboard handles automatically. API keys can be generated in the Dashboard in the 'API Keys' section.

Account

GET/api/account

Returns the authenticated account, its plan and limits, and today's usage.

FieldTypeDescription
idstringUnique account id.
emailstringAccount email.
usernamestringDiscord display name.
planobjectActive plan: name, maxScriptChars, dailyQuota, allowedOptions.
usageobjectToday's usage: used, quota, obfuscationCount, resetsAt.
const res = await fetch("https://moonveil.cc/api/account", {
  headers: { Authorization: `Bearer ${KEY}` },
});

const account = await res.json();
Example response
200 OKapplication/json
{
  "id": "217039...",
  "email": "[email protected]",
  "username": "cpnk",
  "avatar": null,
  "opLevel": 0,
  "hasBilling": true,
  "plan": {
    "name": "Basic",
    "maxScriptChars": 5000000,
    "dailyQuota": 30,
    "allowedOptions": {
      "vms": ["fox", "skid"],
      "compileTypes": ["cff", "vm", "safeEnv"],
    }
  },
  "usage": { "used": 8, "quota": 30, "obfuscationCount": 142, "resetsAt": "2026-06-18T03:21:00Z" }
}

Obfuscate

POST/api/v2/obf

Runs the full obfuscation pipeline over a script. The body takes the source and an optional options object; any option you omit falls back to its default.

FieldTypeDefaultDescription
compileType"cff" | "vm" | "safeEnv""cff"Top-level compile mode for the whole script.
vmType"fox" | "skid""skid"VM implementation, used when the mode is vm or safeEnv or for any MV_VM blocks for cff.
safeEnvLock"luau" | "rbx""luau"Used when the mode is safeEnv, locks to the specified environment.
cffDecomposebooleanfalseDecompose control flow into a flattened state machine.
cffMangleNextbooleanfalseMangle successor / dispatch indices.
cffMangleStringsbooleanfalseMangle string constants.
cffMangleGlobalsbooleanfalseMangle global accesses.
cffMangleCfPercentnumber0Insert fake branches based on the provided chance. Higher = slower.
const obfuscated = await moonveil("obf", {
  script: 'print"meow"',
  options: { compileType: "safeEnv", vmType: "fox", safeEnvLock: "rbx" },
});

Returns the obfuscated Luau as text/plain (sent as a obfuscated.lua attachment). Requesting an option your plan does not allow returns 403.

Example response
200 OKtext/plain
-- This script was generated using MoonVeil 2.0.15-beta
-- (obfuscated bundle, tens of KB, truncated here)
return({...

Prettify

POST/api/v2/prettify

Reformats a script into clean, readable source. The body is just { "script": "..." }. Returns the formatted source as text/plain.

const pretty = await moonveil("prettify", { script });
Example response
200 OKtext/plain
local function verify(pw)
    return pw == "s3cret"
end

Minify

POST/api/v2/minify

Folds constants, strips dead code, and shrinks a script. It runs the optimizer, not just whitespace removal. Body { "script": "..." }, returns text/plain.

const minified = await moonveil("minify", { script });
Example response
200 OKtext/plain
local function verify(pw)return pw=="s3cret"end

Errors

Errors return a JSON envelope with a single error message and a matching status code.

400Malformed JSON, an unknown field, or the script exceeds your character limit.
401Missing or invalid API key / session.
403A requested option is not allowed on your plan.
429Daily quota reached, or you are being rate limited.
500Something failed on our side.

Rate limits

Each plan sets a daily obfuscation quota and a short-window rate limit. Obfuscation counts against both; prettify and minify are only rate limited. When you hit a limit you get 429, with a Retry-After header on the rate-limit case.