LunaUX Decompiler API
Core endpoints for Luau bytecode decompilation, disassembly, and control-flow graph extraction. All endpoints accept POST requests with a JSON body and require a valid API key in the Authorization header.
/api/decompile
Converts Luau bytecode back into human-readable Lua/Luau source code. The output is a plain-text string on success.
Decompile bytecode
POST/api/decompile
Submits base64-encoded Luau bytecode for decompilation. Returns a plain-text Lua source string on HTTP 200. Optionally accepts a filename for debug labeling in the output.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| bytecode | string | required | Luau bytecode encoded as a base64 string (plain text, no data URI prefix). |
| filename | string | optional | Hint name for the script. Used in debug comments in the decompiled output. |
| options | object | optional | Decompile option overrides. See Decompile Options for all available keys. |
{
"bytecode": "TFVBVQEAAAAAAAAAo...",
"filename": "my_script.luau",
"options": {
"EmitTypeAnnotations": false,
"CompoundAssignments": true,
"UpvalueComments": true
}
}
{"error": "...", "detail": "..."}.
-- Decompiled by LunaUX · my_script.luau
local function greet(name) -- line: 1
-- upvalues: none
print("Hello, " .. name)
end
greet("world")
/api/disassemble
Produces a low-level bytecode listing — one instruction per line — without attempting full source reconstruction. Useful for analyzing individual opcodes and register usage.
Disassemble bytecode
POST/api/disassemble
Identical request schema to /api/decompile. Returns a plain-text disassembly listing on HTTP 200.
| Field | Type | Required | Description |
|---|---|---|---|
| bytecode | string | required | Luau bytecode encoded as a base64 string. |
| filename | string | optional | Label name shown at the top of the disassembly output. |
{
"bytecode": "TFVBVQEAAAAAAAAAo...",
"filename": "my_script.luau"
}
{"error": "...", "detail": "..."}.
; proto #0 my_script.luau line 1
[0] LOADK R0, K0 ; "world"
[1] GETGLOBAL R1, K1 ; greet
[2] CALL R1, 1, 1
[3] RETURN R0, 0
/api/cfg
Returns the full control-flow graph (CFG) for each proto (function closure) in the bytecode, as structured JSON. Nodes represent basic blocks; edges represent control-flow transitions.
Extract control-flow graph
POST/api/cfg
Requires only the bytecode field — filename is not used since the response is always JSON. Returns a structured graph object on HTTP 200.
| Field | Type | Required | Description |
|---|---|---|---|
| bytecode | string | required | Luau bytecode encoded as a base64 string. No filename field is accepted. |
{
"bytecode": "TFVBVQEAAAAAAAAAo..."
}
Response schema
Node
Edge
{
"maindebug_id": 0,
"protos": {
"0": {
"debug_name": null,
"line_defined": 0,
"code": {
"entry": "entry",
"nodes": [
{ "id": "entry", "label": "entry", "code": ["LOADK R0, K0", "GETGLOBAL R1, K1", "CALL R1, 1, 1"] },
{ "id": "branch", "label": "branch", "code": ["TEST R0", "JMP cond"] },
{ "id": "then_bb", "label": "then", "code": ["ADD R2, R0, R1", "SETGLOBAL R2"] },
{ "id": "else_bb", "label": "else", "code": ["LOADK R3, K2", "RETURN R3, 1"] },
{ "id": "merge", "label": "merge", "code": ["GETGLOBAL R4, K3", "RETURN R4, 1"] }
],
"edges": [
{ "from": "entry", "to": "branch" },
{ "from": "branch", "to": "then_bb", "label": "T", "kind": "true" },
{ "from": "branch", "to": "else_bb", "label": "F", "kind": "false" },
{ "from": "then_bb", "to": "merge" },
{ "from": "else_bb", "to": "merge" }
]
}
}
}
}
Status Codes
All three endpoints share the same error response format. On success (200) the body varies by endpoint; on any error the body is always JSON.
{"error": "short message", "detail": "extended description"}
Decompile Options
Pass any of these boolean flags in the options object of your /api/decompile request body to control output style. Toggle each option below to compare the two variants.