HTTP REST API
The REST API gives external tools access to the internal state of the emulated machine. All requests require authentication.
This page is the reference for reading; for live documentation, open the
bundled Swagger UI at
http://localhost:8386/api.html while the emulator runs. It shows every
endpoint with schemas from the server's own /openapi.json and can
execute requests against the running machine.
Authentication
Every request must include a bearer token in the Authorization header:
Authorization: Bearer <token>
The token is a random 64-character hex string generated at startup. The
full token is never printed to the log. It can be injected via the
DOSBOX_API_TOKEN environment variable, written to a file, or (as
fallback) the first 8 characters are shown in the log. See the
Webserver page for token provisioning details.
There is no default password and no way to disable authentication.
export TOKEN="your-token"
curl -H "Authorization: Bearer $TOKEN" http://localhost:8386/api/v1/status
Requests without a valid token receive HTTP 401.
Consistency guarantees
All endpoints access the internal state atomically between steps of the emulated CPU. They never execute during natively implemented DOS functions or interrupts, so data structures are guaranteed to be consistent in any given snapshot.
Note
The API has been primarily tested with core = normal. Your experience with
other CPU cores may vary.
Endpoints
System status
GET /api/v1/status
Returns the overall emulator status.
Example input:
curl -H "Authorization: Bearer $TOKEN" http://localhost:8386/api/v1/status
Output:
{
"running": true,
"shutdown_requested": false,
"is_booted": true,
"program": "KEEN4E",
"canonical_name": "KEEN4E.EXE",
"is_shell": false
}
running-- alwaystruewhile the webserver is upshutdown_requested--trueafter a shutdown request has been sentis_booted--trueonce DOS has finished bootingprogram-- the currently running program's segment namecanonical_name-- the running program's canonical filenameis_shell--trueif the DOS shell (command.com) is the active program
GET /api/v1/program/state
Similar to status but focused on the running program.
Example input:
curl -H "Authorization: Bearer $TOKEN" http://localhost:8386/api/v1/program/state
Output:
{
"segment_name": "KEEN4E",
"canonical_name": "KEEN4E.EXE",
"is_shell": false,
"is_booted": true
}
GET /api/v1/dosbox/info
Returns the dosbox-automation version string and the capability features of this build.
Example input:
curl -H "Authorization: Bearer $TOKEN" http://localhost:8386/api/v1/dosbox/info
Output:
{
"version": "0.84-da3",
"features": {
"memory": true,
"input": true,
"cpu_registers": true,
"cpu_control": true,
"port_io": true,
"freeze": true,
"debugger": false
}
}
Clients should check the features block instead of assuming an
endpoint exists; groups can be absent or disabled in future builds. The
debugger group is reserved and reported as false: those routes are
not registered in this release.
POST /api/v1/dosbox/shutdown and POST /api/v1/control/shutdown
Request a graceful shutdown. Both endpoints do the same thing.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" http://localhost:8386/api/v1/dosbox/shutdown
Output:
{
"status": "shutdown_requested"
}CPU registers
GET /api/v1/cpu/state
Returns all x86 CPU registers wrapped in a registers object.
Example input:
curl -H "Authorization: Bearer $TOKEN" http://localhost:8386/api/v1/cpu/state
Output:
{
"registers": {
"eax": 0, "ebx": 0, "ecx": 0, "edx": 0,
"esi": 0, "edi": 0, "esp": 0, "ebp": 0,
"eip": 256, "flags": 0,
"cs": 0, "ds": 0, "es": 0, "ss": 0, "fs": 0, "gs": 0
}
}
PUT /api/v1/cpu/register
Write a single CPU register. Part of the cpu_control capability.
Example input:
curl -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"register": "eax", "value": 4660}' \
http://localhost:8386/api/v1/cpu/register
Output:
{
"status": "ok",
"register": "eax",
"value": 4660
}
The request body takes register and value. Register names are
lowercase, the same set that /api/v1/cpu/state returns.
I/O ports
Direct access to x86 I/O ports, for hardware states the memory and register endpoints cannot reach.
GET /api/v1/io/port
Read a port.
Example input:
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8386/api/v1/io/port?port=0x3C1&width=1"
Output:
{
"port": 961,
"width": 1,
"value": 255
}
Query parameters: port (0x0000 to 0xFFFF, hex with 0x prefix
accepted) and width (1 for byte, the default, or 2 for word).
PUT /api/v1/io/port
Write a port.
Example input:
curl -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"port": 961, "value": 255}' \
http://localhost:8386/api/v1/io/port
The request body takes the same fields: port, value, and an
optional width.
Memory
GET /api/v1/memory/:offset/:len GET /api/v1/memory/:segment/:offset/:len
Read memory at the given address.
Example input:
# Read 256 bytes at linear offset 0x1000
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/memory/0x1000/256 -o mem.bin
# Read 64 bytes at DS:0x100 as JSON
curl -H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
http://localhost:8386/api/v1/memory/DS/0x100/64
# Dump the entire 1 MB conventional memory area
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/memory/0/0x100000 -o conv.bin
The segment parameter is optional and can be a segment register name
(CS, SS, DS, ES, FS, GS) or a numeric value. All URL
parameters accept hex with the 0x prefix.
Returns raw binary by default. Set Accept: application/json to get a JSON
response with Base64-encoded data.
Maximum read size: 128 MiB per request.
PUT /api/v1/memory/:offset PUT /api/v1/memory/:segment/:offset
Write memory at the given address.
Example input:
# Write 4 bytes at offset 0x1000
printf '\x01\x02\x03\x04' | curl -X PUT \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary @- http://localhost:8386/api/v1/memory/0x1000
Accepts raw binary with Content-Type: application/octet-stream, or
JSON with a Base64-encoded data field with
Content-Type: application/json.
Supports atomic compare-and-swap: set the If-Match header to Base64-encoded
expected data. Returns HTTP 412 (Precondition Failed) if the current memory
does not match.
POST /api/v1/memory/allocate
Allocate memory from the emulated machine.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"size": 1024, "area": "conv", "strategy": "best_fit"}' \
http://localhost:8386/api/v1/memory/allocate
Output:
{
"addr": 655360
}
Request fields:
size-- bytes to allocatearea--conv(conventional),UMA(upper memory), orXMSstrategy--best_fit(default),first_fit, orlast_fit. XMS only supportsbest_fit.
Returns HTTP 503 if allocation fails.
POST /api/v1/memory/free
Free previously allocated memory.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"addr": 655360}' \
http://localhost:8386/api/v1/memory/free
The request body takes the addr returned by the allocate call.
Returns HTTP 400 on invalid addresses.
POST /api/v1/memory/search
Scan a physical memory range for a value. The classic trainer workflow: scan for the current value, let it change in the game, scan again among the matches.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": 100, "start": 0, "end": 1048576}' \
http://localhost:8386/api/v1/memory/search
Output:
{
"matches": [65536, 131072]
}
Request fields:
value-- the value to search forwidth-- match width: 1 byte (default), 2 word, or 4 dwordstart,end-- physical scan range, end exclusive. Maximum span 16 MB per request.
POST /api/v1/memory/freeze
Pin a memory address to a value. The emulator rewrites it every frame, the way a trainer holds health or ammo steady.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"address": 65536, "value": 100}' \
http://localhost:8386/api/v1/memory/freeze
The request body takes address, value, and an optional width
(1 byte, the default, 2 word, or 4 dword).
GET /api/v1/memory/freeze lists the active freezes.
DELETE /api/v1/memory/freeze removes one ({"address": 65536}) or,
with an empty body, all of them.
DOS internals
GET /api/v1/dos/internals
Returns pointers to internal DOS data structures and the DOS memory map.
Example input:
curl -H "Authorization: Bearer $TOKEN" http://localhost:8386/api/v1/dos/internals
Output:
{
"listOfLists": 1234,
"dosSwappableArea": 5678,
"firstShell": 2560,
"memoryMap": [
{
"segment": 2064,
"type": "M",
"pspSegment": 8,
"sizeParas": 128,
"sizeBytes": 2048,
"filename": "SC",
"isLast": false
}
]
}
listOfLists-- DOS list of lists (INT 21h AH=52h)dosSwappableArea-- DOS swappable area (INT 21h AX=5D06h)firstShell-- PSP of the first shell (start of usable memory)memoryMap-- the MCB chain, one entry per memory control block: its segment, type (Mfor a chain member,Zfor the last block), owning PSP segment, size in paragraphs and bytes, and the owner filename from the MCB header where DOS records one
Video frame capture
GET /api/v1/video/frame
Captures the current video frame as an image or raw pixel data.
Example input:
# Grab a PNG screenshot
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8386/api/v1/video/frame?format=png" -o frame.png
# Grab a JPEG at lower quality
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8386/api/v1/video/frame?quality=75" -o frame.jpg
The response is the image itself. The output format depends on the
format query parameter or the Accept header:
| Format | Content-Type | Accept header | Example request |
|---|---|---|---|
| JPEG (default) | image/jpeg | none needed | /api/v1/video/frame |
| PNG | image/png | Accept: image/png | /api/v1/video/frame?format=png |
| Raw | application/octet-stream | Accept: application/octet-stream | /api/v1/video/frame?format=raw |
For JPEG, the quality parameter controls compression (1-100, default 98).
The raw format returns a binary header followed by pixel data:
| Field | Type | Description |
|---|---|---|
| width | uint32 | Frame width in pixels |
| height | uint32 | Frame height in pixels |
| pitch | int32 | Bytes per row (may be negative for bottom-up) |
| pixel_format | uint8 | Format enum (see below) |
| palette_count | uint16 | Number of palette entries (256 for indexed, 0 otherwise) |
| palette | 3 bytes x count | RGB triplets, only present if palette_count > 0 |
| data | width x height x bpp | Raw pixel data |
Pixel formats: Indexed8, RGB555_Packed16, RGB565_Packed16,
BGR24_ByteArray, BGRX32_ByteArray.
The mode parameter selects the frame source, the same way it does
for video capture: raw (default) is the
emulator framebuffer at native resolution, rendered is the
post-shader image as shown on screen at window resolution. A rendered
grab waits for the next presented frame, so it returns 503 when
nothing is being presented (paused or minimized emulator). Neither
mode includes the on-screen automation overlay. See
raw vs rendered
for when to use which.
# Grab the rendered display as shown on screen
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8386/api/v1/video/frame?format=png&mode=rendered" -o shown.pngInfo
Returns HTTP 503 if no frame is available yet (emulator still starting).
GET /api/v1/video/frame/info
Returns frame metadata without the actual pixel data.
Example input:
curl -H "Authorization: Bearer $TOKEN" http://localhost:8386/api/v1/video/frame/info
Output:
{
"width": 320,
"height": 200,
"pixel_format": "Indexed8",
"pitch": 320,
"is_paletted": true,
"video_mode": {
"width": 320,
"height": 200,
"is_graphics_mode": true,
"is_double_scanned": true,
"graphics_standard": "VGA",
"color_depth": "IndexedColor8Bit",
"bios_mode_number": 19
},
"rendered_double_scan": false,
"double_width": false,
"double_height": false
}Screen text
GET /api/v1/video/text
Read the text-mode screen as characters.
Example input:
curl -H "Authorization: Bearer $TOKEN" http://localhost:8386/api/v1/video/text
Output:
{
"is_text_mode": true,
"bios_mode": 3,
"columns": 80,
"rows": 25,
"page": 0,
"text": "Setup ver 1.666 (C) 1994 id Software\n"
}
The response carries the character buffer as UTF-8 with CP437
box-drawing and shade glyphs preserved, plus the mode geometry. In a
graphics mode, text is empty and is_text_mode is false. Use this
to drive installers by what is actually on screen instead of fixed
timings.
Works across CGA, Hercules, Tandy, and VGA text modes; the reader follows the active video page.
Input injection and recording
Sending input
POST /api/v1/input/sequence
Send a sequence of keyboard and mouse events into the emulated DOS environment. Events are dispatched in order, timed by either PIC emulation ticks or frame numbers.
Type "Y" followed by Enter, with a 2-second pause before the Enter:
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"events": [
{"type": "key", "key": "KBD_y", "pressed": true, "t": 0},
{"type": "key", "key": "KBD_y", "pressed": false, "t": 50},
{"type": "key", "key": "KBD_enter", "pressed": true, "t": 2000},
{"type": "key", "key": "KBD_enter", "pressed": false, "t": 2050}
]
}' http://localhost:8386/api/v1/input/sequence
Output:
{
"status": "ok",
"events_scheduled": 4
}
Returns HTTP 409 if a replay is already in progress.
Each event has these fields:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | yes | key, mouse_move, mouse_button, or mouse_wheel |
t | float | no | Timestamp in milliseconds from sequence start. Events at t=0 are dispatched immediately. |
delay_ms | float | no | Delay in milliseconds after the previous event. The natural form for hand-written sequences; mutually exclusive with absolute timing per event. |
frame | uint64 | no | Frame number. If any event has a frame field, the entire sequence uses frame-based replay. |
key | string | for key | Key name, e.g. KBD_enter, KBD_a, KBD_f1. See key name table below. |
pressed | bool | no | true for press, false for release. Default true. |
button | string | for mouse_button | left, right, or middle |
x_rel, y_rel | float | for mouse_move | Relative mouse movement |
x_abs, y_abs | float | no | Absolute mouse position |
delta | float | for mouse_wheel | Scroll wheel delta |
Maximum 32000 events per request. Events with unknown fields are rejected with an error naming the allowed ones, so a typo in a field name fails loudly instead of injecting a zero-motion event.
Timing modes: If events include frame fields, replay is frame-based:
events fire when the emulator reaches the specified frame number. This gives
deterministic replay regardless of host speed. Without frame fields, events
are timed via the PIC (Programmable Interrupt Controller) emulation clock,
which measures milliseconds of emulated time.
Warning
Replay determinism and randomness. Deterministic replay depends on the emulated program producing the same output for the same input. Games that use hardware timers, RNG seeded from real time, or nondeterministic behavior (Dr. Riptide, for example, randomizes enemy patterns) will diverge between runs even with identical input sequences. For these games, frame-based replay is more resilient than PIC-based, but not immune. Automated testing of such games should verify screen state between steps rather than relying on replaying a fixed recording blindly.
Warning
Replay needs the same machine settings. A recording only replays
faithfully in the environment it was made in. Fix cpu_cycles and
cpu_cycles_protected to the same values for the recording and every
replay: with the default auto behavior the CPU speed ramps differently
between runs, and frame-indexed events land on the wrong game state. The
same goes for keyboard_layout. Recordings store raw key positions, and
the active DOS layout decides which characters they produce, so a
recording made under the US layout types garbage on a system that
auto-detects a German one. Pin both settings in the config instead of
leaving them on auto, and note them alongside the recording.
POST /api/v1/input/type
Type a string with paced key injection.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"text": "dir /w"}' \
http://localhost:8386/api/v1/input/type
Output:
{
"status": "ok",
"chars": 6
}
The characters are injected as paced key presses against the emulated
keyboard controller's buffer, the same mechanism the Lua type()
function uses, so nothing gets dropped no matter how long the string
is. Request fields:
text-- the string to type, up to 4096 characterscps-- typing speed in characters per second, default 30
Recording input
Record keyboard and mouse events as they happen, then retrieve the recording for later replay.
POST /api/v1/input/record/start
Start recording.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/input/record/start
Output:
{
"status": "recording"
}
Events are captured with both PIC timestamps (milliseconds) and frame numbers, so the same recording can be replayed in either timing mode. Returns HTTP 409 if already recording.
POST /api/v1/input/record/pause
Toggle pause. While paused, events are not captured.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/input/record/pause
Output:
{
"status": "paused"
}
Or "recording" if unpaused. Returns HTTP 409 if no recording is active.
POST /api/v1/input/record/stop
Stop recording and return all captured events.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/input/record/stop
Output:
{
"event_count": 127,
"duration_ms": 15230.5,
"events": [
{"t": 0, "frame": 0, "type": "key", "key": "KBD_enter", "pressed": true},
{"t": 48.3, "frame": 3, "type": "key", "key": "KBD_enter", "pressed": false},
{"t": 512.1, "frame": 31, "type": "mouse_move", "x_rel": 3.0, "y_rel": -1.5, "x_abs": 160.0, "y_abs": 100.0},
{"t": 1024.7, "frame": 61, "type": "mouse_button", "button": "left", "pressed": true}
]
}
The response contains the full event sequence in the same format
accepted by /api/v1/input/sequence, so it can be fed back directly
for replay. Each event includes both t (PIC milliseconds) and frame
(rendered frame count). When replaying, the presence of frame fields
in the sequence triggers frame-based replay automatically. Returns
HTTP 409 if no recording is active.
GET /api/v1/input/record/status
Check recording state without stopping.
Example input:
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/input/record/status
Output:
{
"recording": true,
"paused": false,
"event_count": 42,
"duration_ms": 3150.2
}Lua scripting
Load and run sandboxed Lua scripts inside the emulator. Scripts run as coroutines, yielding back to the emulator between frames. See the Lua scripting page for the full API available to scripts.
POST /api/v1/script/load
Load a Lua script from the request body.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/plain" \
--data-binary @myscript.lua \
"http://localhost:8386/api/v1/script/load?name=my-test-script&debug=true"
Output:
{
"status": "loaded",
"name": "my-test-script"
}
The script source is sent as text/plain or application/x-lua.
Parameters are passed as query strings:
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | no | Script name (alphanumeric, hyphens, underscores, max 64 chars). Defaults to "unnamed". |
seed | int64 | no | RNG seed for deterministic runs. |
debug | bool | no | Enable debug log output to the config logs/ directory. |
Returns HTTP 400 if a script is already running, the body is empty, or validation fails. Returns HTTP 429 if called within 2 seconds of the previous load (rate limited).
POST /api/v1/script/start
Start the previously loaded script.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/script/start
Output:
{
"status": "started"
}
The script runs as a coroutine, dispatched once per frame. Returns HTTP 400 if no script is loaded or a script is already running.
POST /api/v1/script/stop
Stop the running script and close any open debug log.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/script/stop
Output:
{
"status": "stopped"
}
GET /api/v1/script/status
Check the state of the scripting engine.
Example input:
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/script/status
Output:
{
"state": "running",
"frame": 1842,
"name": "my-test-script"
}
When a script finishes or errors:
{
"state": "error",
"frame": 503,
"name": "my-test-script",
"error": "script exceeded instruction limit",
"output": {"result": "partial data"}
}
The output field contains whatever the script wrote to the
dosbox.output table, serialized as JSON. Possible state values:
idle, loaded, running, yielded, completed, error.
Video capture (ZMBV)
Record the emulated screen to a ZMBV-encoded AVI file.
POST /api/v1/capture/video/start
Start video capture. The output file is written to the capture directory.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"mode": "rendered"}' \
http://localhost:8386/api/v1/capture/video/start
Output:
{
"status": "recording"
}
The optional request body selects what feeds the encoder via mode:
raw(default) -- the emulator framebuffer at native resolution, frames as the emulator produces themrendered-- the post-shader image as shown on screen, at window resolution and a constant frame rate
If rendered recording causes stutter at large window sizes, lower its compression level (see the compression endpoint below). Output is ZMBV in both modes; transcode with ffmpeg if another codec is needed. See raw vs rendered for when to use which.
POST /api/v1/capture/video/stop
Stop video capture and finalize the AVI file.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/capture/video/stop
Output:
{
"status": "stopped"
}
GET /api/v1/capture/video/status
Check whether video capture is currently active.
Example input:
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/capture/video/status
Output:
{
"capturing": true,
"mode": "raw",
"last_stop_reason": "none"
}
last_stop_reason explains why the previous recording ended: none,
clean, write_error, or disk_space_low. A recording stops on its
own when the capture drive's free space falls below
capture_min_free_space_mb, and a write error (full disk, removed
device) ends it with a playable file up to the truncation point instead
of a corrupt one.
GET /api/v1/capture/video/compression
Read the zlib compression levels used for video capture, one per capture mode.
Example input:
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/capture/video/compression
Output:
{
"raw": 9,
"rendered": 6
}
PUT /api/v1/capture/video/compression
Set the compression level for one or both capture modes, 0 (store
only) to 9 (maximum). The level is applied when the next recording
starts. While a recording is running the request is refused with
409, because the running recording keeps the level it started with.
Example input:
curl -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"rendered": 2}' \
http://localhost:8386/api/v1/capture/video/compression
Output:
{
"raw": 9,
"rendered": 2
}
The response always reports both resulting levels. The startup values
come from the capture_video_compression and
capture_video_compression_rendered config settings; see the
capture settings.
Drive swap
POST /api/v1/drive/swap
Swap a mounted disk image on a drive letter. Used for multi-disk game installs where the installer asks to insert the next disk.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"drive": "A", "image": "/games/monkey-island/disk2.img"}' \
http://localhost:8386/api/v1/drive/swap
Output:
{
"status": "ok",
"drive": "A"
}
Request fields:
drive-- single letter, A through Zimage-- absolute path to the disk image file on the host filesystem
The current drive is unmounted and replaced with the new image. The endpoint autodetects floppy images by matching the file size against known floppy geometries (360K, 720K, 1.2M, 1.44M, 2.88M). Anything else is mounted as a hard disk image.
The image path is validated against the mount policy before mounting: it must
be a regular file (no symlinks, no system paths) with valid disk image
structure. If mount_allowed_image_roots is configured, the image must be
under one of those directories. Images are mounted read-only.
Returns HTTP 403 if the mount lock is engaged. Returns HTTP 400 if the file does not exist, the path is blocked by the mount policy, the drive letter is invalid, or the image fails to mount.
Example: three-disk install automation
# Installer is running from disk 1 on A:, asks for disk 2
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"drive": "A", "image": "/games/monkey-island/disk2.img"}' \
http://localhost:8386/api/v1/drive/swap
# Send Enter to continue the installer
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"events": [{"type": "key", "key": "KBD_enter", "pressed": true, "t": 0}, {"type": "key", "key": "KBD_enter", "pressed": false, "t": 50}]}' \
http://localhost:8386/api/v1/input/sequence
# When it asks for disk 3
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"drive": "A", "image": "/games/monkey-island/disk3.img"}' \
http://localhost:8386/api/v1/drive/swapMount lock
POST /api/v1/mount/lock
Engage the mount lock.
Example input:
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/mount/lock
Output:
{
"status": "locked"
}
Once locked, all mount operations are refused: directory mounts, image mounts, and BOOT. The lock is one-way and cannot be reversed without restarting the emulator.
Launchers should call this after the installation phase completes. For multi-disc games, all disc swaps must happen before locking.
GET /api/v1/mount/lock
Check the current lock state.
Example input:
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8386/api/v1/mount/lock
Output:
{
"locked": false
}Key names
The input endpoints use key names with the KBD_ prefix. Here are the most
commonly used ones:
| Key name | Key |
|---|---|
KBD_a through KBD_z | Letters |
KBD_0 through KBD_9 | Number row |
KBD_f1 through KBD_f12 | Function keys |
KBD_enter | Enter/Return |
KBD_space | Spacebar |
KBD_esc | Escape |
KBD_tab | Tab |
KBD_backspace | Backspace |
KBD_up, KBD_down, KBD_left, KBD_right | Arrow keys |
KBD_insert, KBD_delete, KBD_home, KBD_end | Navigation |
KBD_pageup, KBD_pagedown | Page up/down |
KBD_leftshift, KBD_rightshift | Shift keys |
KBD_leftctrl, KBD_rightctrl | Ctrl keys |
KBD_leftalt, KBD_rightalt | Alt keys |
KBD_capslock, KBD_numlock, KBD_scrolllock | Lock keys |
KBD_kp0 through KBD_kp9 | Numpad digits |
KBD_kpenter, KBD_kpplus, KBD_kpminus | Numpad operators |
KBD_kpmultiply, KBD_kpdivide, KBD_kpperiod | Numpad operators |
KBD_minus, KBD_equals, KBD_grave | Punctuation |
KBD_comma, KBD_period, KBD_slash | Punctuation |
KBD_semicolon, KBD_quote | Punctuation |
KBD_leftbracket, KBD_rightbracket, KBD_backslash | Brackets |
KBD_leftgui, KBD_rightgui | Windows/Super keys |
KBD_oem102 | OEM 102 key (non-US keyboards) |
KBD_abnt1 | Brazilian ABNT layout key |
KBD_printscreen, KBD_pause | Special keys |
Example tools
The extras/api/ directory in the dosbox-automation source tree contains
ready-to-use HTML tools and a JavaScript API wrapper. Copy them to the
webserver directory inside your configuration folder to use them.
-
Memory Monitor -- watch and edit memory locations live. Import a config string to set up named watch addresses. Try it with Commander Keen 4: run the game, import the config from the source tree, and watch lives/ammo update in real time. You can edit the values too.
-
Memory Scanner -- find where specific values are stored. Search for a known value (like your current ammo count), change it in the game, filter the results. Repeat until you have isolated the memory address. Same workflow as Cheat Engine, but running against an emulated machine.
-
Memory Viewer -- hex viewer with a built-in x86 disassembler.
-
JavaScript API wrapper (
api.js) -- a JavaScript class for building custom tools. Handles Base64 encoding/decoding, segment register resolution, and compare-and-swap operations. TypeScript definitions inapi.d.ts.