---
title: "Servers API: Power, Console, Startup"
description: "Silly Development API reference for servers: list and read them, start, stop and restart, send console commands, the live console WebSocket and startup."
url: "https://sillydev.co.uk/docs/panel/servers"
updated: "2026-10-10"
---

API reference

# Servers, power and console

List your servers, start and stop them, send console commands, watch the console live, and change how they start.

[← API docs](https://sillydev.co.uk/docs/panel) · 20 endpoints · Updated 10 October 2026

**Browse the docs**

## Servers

List the servers your key can reach, read one server's details, resource use and activity, and delete a server you own.

### List servers

GET`/`

Lists the servers the key's user owns or is a subuser of, 50 a page by default and at most 100. A key limited to some servers only lists those. The default includes `allocations` and `variables` are always present, and `egg` and `subusers` can be added with `include`. If the account can buy paid servers, `meta.pending_orders` lists paid servers that have been bought but do not exist yet.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| page | integer | The page of results to return, starting at 1. |
| per_page | integer | How many servers to return on each page. Values above 100 are treated as 100. |
| type | one of 'owner', 'admin', 'admin-all' | Which servers to list. Leave it out for every server you own or are a subuser of; `owner` lists only the servers you own. `admin` (servers you can reach only as an administrator) and `admin-all` (every server) are for administrators: anyone else gets an empty list, and a key limited to some servers or permissions ignores them. |
| filter\[\*\] | string | A general search. An IPv4 address, `ip:port` or `:port` matches the server's allocations; anything else matches the start of the UUID, the 8-character identifier, the external ID, or part of the name (case-insensitive). |
| filter\[uuid\] | string | Servers whose UUID contains this text. |
| filter\[name\] | string | Servers whose name contains this text. |
| filter\[description\] | string | Servers whose description contains this text. |
| filter\[external_id\] | string | Servers whose external ID contains this text. |
| include | string | Extra relationships to add to each server, comma-separated: `egg` and/or `subusers`. `subusers` comes back as a `null_resource` on servers where you lack `user.read`. |

Example

```
curl "https://sillydev.co.uk/panel/api/client/" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 200A page of servers.

```
{
    "object": "list",
    "data": [
        {
            "object": "server",
            "attributes": {
                "server_owner": true,
                "identifier": "1a7ce997",
                "__deprecated_uuid_short": "1a7ce997",
                "server_identifier": "serv_DJ5MXFRFJ5JFFDSOJZHE5RQCUU",
                "internal_id": 4127,
                "uuid": "1a7ce997-259b-452e-8b4e-cecc464142ca",
                "name": "My Discord bot",
                "node": "London 3",
                "is_node_under_maintenance": false,
                "sftp_details": {
                    "ip": "lon3.sillydev.co.uk",
                    "port": 2022
                },
                "description": "",
                "limits": {
                    "memory": 256,
                    "swap": 0,
                    "disk": 512,
                    "io": 500,
                    "cpu": 25,
                    "threads": null,
                    "oom_disabled": true
                },
                "invocation": "node /home/container/index.js",
                "docker_image": "ghcr.io/pterodactyl/yolks:nodejs_22",
                "egg_features": null,
                "feature_limits": {
                    "allocations": 1,
                    "backups": 2
                },
                "status": null,
                "is_paid": false,
                "is_suspended": false,
                "is_installing": false,
                "is_transferring": false,
                "skip_scripts": false,
                "relationships": {
                    "allocations": {
                        "object": "list",
                        "data": [
                            {
                                "object": "allocation",
                                "attributes": {
                                    "id": 9921,
                                    "ip": "203.0.113.10",
                                    "ip_alias": null,
                                    "port": 25565,
                                    "notes": null,
                                    "is_default": true
                                }
                            }
                        ]
                    },
                    "variables": {
                        "object": "list",
                        "data": [
                            {
                                "object": "egg_variable",
                                "attributes": {
                                    "name": "Main file",
                                    "description": "The file the bot is started from.",
                                    "env_variable": "MAIN_FILE",
                                    "default_value": "index.js",
                                    "server_value": "index.js",
                                    "is_editable": true,
                                    "rules": "required|string|max:64"
                                }
                            }
                        ]
                    }
                }
            }
        }
    ],
    "meta": {
        "pagination": {
            "total": 1,
            "count": 1,
            "per_page": 50,
            "current_page": 1,
            "total_pages": 1,
            "links": []
        },
        "pending_orders": [
            {
                "uuid": "6f1d0c2e-8a7b-4c33-9b1e-2f0a5d4c7e91",
                "name": "My paid bot",
                "runtime": "python",
                "status": "provisioning",
                "attempts": 1,
                "limits": {
                    "memory": 1024,
                    "disk": 2048,
                    "cpu": 100
                },
                "created_at": "2026-10-10T12:00:00+00:00"
            }
        ]
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| objectrequired | string |  |
| datarequired | array of object |  |
| data\[\].objectrequired | string |  |
| data\[\].attributesrequired | object | A server, as the key's user sees it. |
| data\[\].attributes.server_ownerrequired | boolean | Whether the key's user owns this server (rather than being a subuser or administrator). |
| data\[\].attributes.identifierrequired | string | The 8-character identifier used in panel URLs and accepted by the `{server}` path parameter. |
| data\[\].attributes.\_\_deprecated_uuid_shortrequired | string | The same as `identifier`. |
| data\[\].attributes.server_identifierrequired | string | A prefixed internal identifier (`serv_...`). Not accepted in paths; use `identifier` or `uuid`. |
| data\[\].attributes.internal_idrequired | integer | The server's numeric database ID. |
| data\[\].attributes.uuidrequired | string (uuid) |  |
| data\[\].attributes.namerequired | string |  |
| data\[\].attributes.noderequired | string | The name of the node the server runs on. |
| data\[\].attributes.is_node_under_maintenancerequired | boolean | While true, most endpoints for the server answer 409. |
| data\[\].attributes.sftp_detailsrequired | object |  |
| data\[\].attributes.sftp_details.iprequired | string | The SFTP host name. |
| data\[\].attributes.sftp_details.portrequired | integer |  |
| data\[\].attributes.descriptionrequired | string \| null |  |
| data\[\].attributes.limitsrequired | object |  |
| data\[\].attributes.limits.memoryrequired | integer | Memory limit in MiB. 0 means unlimited. |
| data\[\].attributes.limits.swaprequired | integer | Swap in MiB. 0 means none and -1 unlimited. |
| data\[\].attributes.limits.diskrequired | integer | Disk limit in MiB. 0 means unlimited. |
| data\[\].attributes.limits.iorequired | integer | Block IO weight, 10 to 1000. |
| data\[\].attributes.limits.cpurequired | integer | CPU limit as a percentage of one core (100 is one core). 0 means unlimited. |
| data\[\].attributes.limits.threadsrequired | string \| null | The CPU cores the server is pinned to, e.g. `0-1,3`, or null for any. |
| data\[\].attributes.limits.oom_disabledrequired | boolean | Whether the out-of-memory killer is disabled for the server. |
| data\[\].attributes.invocationrequired | string | The startup command with variables filled in. Without `startup.read` the variable values are hidden. |
| data\[\].attributes.docker_imagerequired | string |  |
| data\[\].attributes.egg_featuresrequired | array of string \| null | Panel features the server's egg turns on. |
| data\[\].attributes.feature_limitsrequired | object |  |
| data\[\].attributes.feature_limits.allocationsrequired | integer | The most allocations (ports) the server may have. |
| data\[\].attributes.feature_limits.backupsrequired | integer | The most backups the server may keep. |
| data\[\].attributes.statusrequired | one of null, 'installing', 'install_failed', 'reinstall_failed', 'suspended', 'billing_suspended', 'restoring_backup', 'deleting' | What the server is doing, or null when it is installed and available. - `installing`: the install script is running. - `install_failed` / `reinstall_failed`: the install script failed. A server whose first install failed can still be reinstalled. - `suspended`: suspended by an administrator. - `billing_suspended`: paused because its subscription is unpaid. - `restoring_backup`: a backup is being restored. - `deleting`: deletion has been accepted and the node is removing it. |
| data\[\].attributes.is_paidrequired | boolean | Whether someone is currently paying for the server. |
| data\[\].attributes.is_suspendedrequired | boolean | True when `status` is `suspended` or `billing_suspended`. Use `status`. |
| data\[\].attributes.is_installingrequired | boolean | True when `status` is `installing` or `install_failed`. Use `status`. |
| data\[\].attributes.is_transferringrequired | boolean | Whether the server is being moved to another node. Most endpoints answer 409 while it is. |
| data\[\].attributes.skip_scriptsrequired | boolean | Whether the egg's install script is skipped for this server. |
| data\[\].attributes.relationships | object |  |
| data\[\].attributes.relationships.allocations | object | The server's allocations. Always included. Without `allocation.read` only the primary allocation is listed, with `notes` null. |
| data\[\].attributes.relationships.variables | any | The startup variables you can see. Always included; a `null_resource` without `startup.read`. |
| data\[\].attributes.relationships.egg | object | The egg (runtime template) the server uses. Only with `include=egg`. |
| data\[\].attributes.relationships.subusers | any | The server's subusers. Only with `include=subusers`; a `null_resource` without `user.read`. |
| metarequired | object |  |
| meta.paginationrequired | object |  |
| meta.pagination.total | integer |  |
| meta.pagination.count | integer |  |
| meta.pagination.per_page | integer |  |
| meta.pagination.current_page | integer |  |
| meta.pagination.total_pages | integer |  |
| meta.pagination.links | object |  |
| meta.pagination.links.previous | string (uri) |  |
| meta.pagination.links.next | string (uri) |  |
| meta.pending_orders | array of object | Paid servers you have bought that do not exist yet. Only present when the account can buy paid servers. |
| meta.pending_orders\[\].uuidrequired | string (uuid) | The order's UUID (not a server UUID). |
| meta.pending_orders\[\].namerequired | string |  |
| meta.pending_orders\[\].runtimerequired | string \| null | The runtime chosen, e.g. `nodejs`, `python` or `bun`. |
| meta.pending_orders\[\].statusrequired | one of 'provisioning', 'pending_deployment' | `provisioning` while the server is being created, `pending_deployment` while it waits for room on a node. |
| meta.pending_orders\[\].attemptsrequired | integer | How many times creating the server has been tried. |
| meta.pending_orders\[\].limitsrequired | object | The paid plan's limits. |
| meta.pending_orders\[\].limits.memoryrequired | integer | MiB. |
| meta.pending_orders\[\].limits.diskrequired | integer | MiB. |
| meta.pending_orders\[\].limits.cpurequired | integer | Percent of one core. |
| meta.pending_orders\[\].created_atrequired | string (date-time) \| null |  |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### List subuser permissions

GET`/permissions`

Returns every server permission that exists, grouped, with a description of each. Use it to build a permission picker or to check the strings used in `x-permission` and in subuser and API key permission lists; a permission is written `group.key`, e.g. `control.start`.

Example

```
curl "https://sillydev.co.uk/panel/api/client/permissions" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 200All server permissions.

```
{
    "object": "system_permissions",
    "attributes": {
        "permissions": {
            "websocket": {
                "description": "Allows the user to connect to the server websocket, giving them access to view console output and realtime server stats.",
                "keys": {
                    "connect": "Allows a user to connect to the websocket instance for a server to stream the console."
                }
            },
            "control": {
                "description": "Permissions that control a user's ability to control the power state of a server, or send commands.",
                "keys": {
                    "console": "Allows a user to send commands to the server instance via the console.",
                    "start": "Allows a user to start the server if it is stopped.",
                    "stop": "Allows a user to stop a server if it is running.",
                    "restart": "Allows a user to perform a server restart. This allows them to start the server if it is offline, but not put the server in a completely stopped state."
                }
            }
        }
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| objectrequired | string |  |
| attributesrequired | object |  |
| attributes.permissionsrequired | object | Permission groups by name (e.g. `control`, `file`). |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.

### Get resource use of several servers

GET`/resources`

Returns the live resource use of up to 100 servers in one request, keyed by server UUID. A server is silently left out if you could not read it on its own, if it is suspended, installing, being transferred, restoring a backup, being deleted or on a node under maintenance, or if its node did not answer. Answers are cached for up to 20 seconds (3 seconds while a server is booting), so polling faster gains nothing.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serversrequired | string | Comma-separated full server UUIDs (not 8-character identifiers). Duplicates are ignored and only the first 100 are used. |

Example

```
curl "https://sillydev.co.uk/panel/api/client/resources" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 200The usage of each server that could be read, keyed by UUID. When none could be read, \`data\` is an empty object.

```
{
    "object": "server_stats_list",
    "data": {
        "1a7ce997-259b-452e-8b4e-cecc464142ca": {
            "object": "stats",
            "attributes": {
                "current_state": "running",
                "is_suspended": false,
                "is_booting": false,
                "resources": {
                    "memory_bytes": 83886080,
                    "cpu_absolute": 1.25,
                    "disk_bytes": 41943040,
                    "network_rx_bytes": 102400,
                    "network_tx_bytes": 51200,
                    "uptime": 3600000
                }
            }
        }
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| objectrequired | string |  |
| datarequired | object |  |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 422 Some fields were missing or invalid. There is one error per problem, and `meta.source_field` names the field.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### Get a server

GET`/servers/{server}`

Returns one server with its allocations and startup variables. This works whatever state the server is in (installing, suspended, being transferred or being deleted), so it is the endpoint to poll while waiting for a state to change. `meta` says whether you own the server and which permissions you (as limited by your key) have on it; `*` means all of them.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |
| include | string | Extra relationships, comma-separated: `egg` and/or `subusers`. `subusers` comes back as a `null_resource` if you lack `user.read`. |

Example

```
curl "https://sillydev.co.uk/panel/api/client/servers/1a7ce997" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 200The server.

```
{
    "object": "server",
    "attributes": {
        "server_owner": true,
        "identifier": "1a7ce997",
        "__deprecated_uuid_short": "1a7ce997",
        "server_identifier": "serv_DJ5MXFRFJ5JFFDSOJZHE5RQCUU",
        "internal_id": 4127,
        "uuid": "1a7ce997-259b-452e-8b4e-cecc464142ca",
        "name": "My Discord bot",
        "node": "London 3",
        "is_node_under_maintenance": false,
        "sftp_details": {
            "ip": "lon3.sillydev.co.uk",
            "port": 2022
        },
        "description": "Moderation bot for my server",
        "limits": {
            "memory": 256,
            "swap": 0,
            "disk": 512,
            "io": 500,
            "cpu": 25,
            "threads": null,
            "oom_disabled": true
        },
        "invocation": "python /home/container/bot.py",
        "docker_image": "ghcr.io/pterodactyl/yolks:python_3.12",
        "egg_features": null,
        "feature_limits": {
            "allocations": 1,
            "backups": 2
        },
        "status": null,
        "is_paid": false,
        "is_suspended": false,
        "is_installing": false,
        "is_transferring": false,
        "skip_scripts": false,
        "relationships": {
            "allocations": {
                "object": "list",
                "data": [
                    {
                        "object": "allocation",
                        "attributes": {
                            "id": 9921,
                            "ip": "203.0.113.10",
                            "ip_alias": null,
                            "port": 25565,
                            "notes": null,
                            "is_default": true
                        }
                    }
                ]
            },
            "variables": {
                "object": "list",
                "data": [
                    {
                        "object": "egg_variable",
                        "attributes": {
                            "name": "Bot file",
                            "description": "The Python file the bot is started from.",
                            "env_variable": "BOT_PY_FILE",
                            "default_value": "bot.py",
                            "server_value": "bot.py",
                            "is_editable": true,
                            "rules": "required|string|max:64"
                        }
                    }
                ]
            },
            "egg": {
                "object": "egg",
                "attributes": {
                    "uuid": "3e9d5a1c-7f42-4b8e-a1d6-0c5b9f2e8d47",
                    "name": "Python"
                }
            }
        }
    },
    "meta": {
        "is_server_owner": true,
        "user_permissions": [
            "*"
        ]
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| objectrequired | string |  |
| attributesrequired | object | A server, as the key's user sees it. |
| attributes.server_ownerrequired | boolean | Whether the key's user owns this server (rather than being a subuser or administrator). |
| attributes.identifierrequired | string | The 8-character identifier used in panel URLs and accepted by the `{server}` path parameter. |
| attributes.\_\_deprecated_uuid_shortrequired | string | The same as `identifier`. |
| attributes.server_identifierrequired | string | A prefixed internal identifier (`serv_...`). Not accepted in paths; use `identifier` or `uuid`. |
| attributes.internal_idrequired | integer | The server's numeric database ID. |
| attributes.uuidrequired | string (uuid) |  |
| attributes.namerequired | string |  |
| attributes.noderequired | string | The name of the node the server runs on. |
| attributes.is_node_under_maintenancerequired | boolean | While true, most endpoints for the server answer 409. |
| attributes.sftp_detailsrequired | object |  |
| attributes.sftp_details.iprequired | string | The SFTP host name. |
| attributes.sftp_details.portrequired | integer |  |
| attributes.descriptionrequired | string \| null |  |
| attributes.limitsrequired | object |  |
| attributes.limits.memoryrequired | integer | Memory limit in MiB. 0 means unlimited. |
| attributes.limits.swaprequired | integer | Swap in MiB. 0 means none and -1 unlimited. |
| attributes.limits.diskrequired | integer | Disk limit in MiB. 0 means unlimited. |
| attributes.limits.iorequired | integer | Block IO weight, 10 to 1000. |
| attributes.limits.cpurequired | integer | CPU limit as a percentage of one core (100 is one core). 0 means unlimited. |
| attributes.limits.threadsrequired | string \| null | The CPU cores the server is pinned to, e.g. `0-1,3`, or null for any. |
| attributes.limits.oom_disabledrequired | boolean | Whether the out-of-memory killer is disabled for the server. |
| attributes.invocationrequired | string | The startup command with variables filled in. Without `startup.read` the variable values are hidden. |
| attributes.docker_imagerequired | string |  |
| attributes.egg_featuresrequired | array of string \| null | Panel features the server's egg turns on. |
| attributes.feature_limitsrequired | object |  |
| attributes.feature_limits.allocationsrequired | integer | The most allocations (ports) the server may have. |
| attributes.feature_limits.backupsrequired | integer | The most backups the server may keep. |
| attributes.statusrequired | one of null, 'installing', 'install_failed', 'reinstall_failed', 'suspended', 'billing_suspended', 'restoring_backup', 'deleting' | What the server is doing, or null when it is installed and available. - `installing`: the install script is running. - `install_failed` / `reinstall_failed`: the install script failed. A server whose first install failed can still be reinstalled. - `suspended`: suspended by an administrator. - `billing_suspended`: paused because its subscription is unpaid. - `restoring_backup`: a backup is being restored. - `deleting`: deletion has been accepted and the node is removing it. |
| attributes.is_paidrequired | boolean | Whether someone is currently paying for the server. |
| attributes.is_suspendedrequired | boolean | True when `status` is `suspended` or `billing_suspended`. Use `status`. |
| attributes.is_installingrequired | boolean | True when `status` is `installing` or `install_failed`. Use `status`. |
| attributes.is_transferringrequired | boolean | Whether the server is being moved to another node. Most endpoints answer 409 while it is. |
| attributes.skip_scriptsrequired | boolean | Whether the egg's install script is skipped for this server. |
| attributes.relationships | object |  |
| attributes.relationships.allocations | object | The server's allocations. Always included. Without `allocation.read` only the primary allocation is listed, with `notes` null. |
| attributes.relationships.allocations.objectrequired | string |  |
| attributes.relationships.allocations.datarequired | array of object |  |
| attributes.relationships.variables | any | The startup variables you can see. Always included; a `null_resource` without `startup.read`. |
| attributes.relationships.egg | object | The egg (runtime template) the server uses. Only with `include=egg`. |
| attributes.relationships.egg.objectrequired | string |  |
| attributes.relationships.egg.attributesrequired | object |  |
| attributes.relationships.egg.attributes.uuidrequired | string (uuid) |  |
| attributes.relationships.egg.attributes.namerequired | string |  |
| attributes.relationships.subusers | any | The server's subusers. Only with `include=subusers`; a `null_resource` without `user.read`. |
| metarequired | object |  |
| meta.is_server_ownerrequired | boolean | Whether the key's user owns this server. |
| meta.user_permissionsrequired | array of string | The permissions you have on this server. Owners and administrators get `["*"]` (administrators also get `admin.websocket.*` entries), subusers get their list, and a key limited to some permissions only gets those. |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### Delete a server

DELETE`/servers/{server}`

Permanently deletes the server and its files. Only the server's owner can do this, with a key that is not limited to a set of permissions; subusers cannot, and it cannot be delegated. Deletion is asynchronous: once the node accepts it the server's `status` becomes `deleting` and every other endpoint for it answers 409 until the node has removed it, after which it is gone (GET returns 404). If the node cannot be reached nothing is deleted and the error is returned. A paid server whose subscription is still set to renew is refused with 400: schedule a downgrade first.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Body

| Name | Type | Description |
| --- | --- | --- |
| namerequired | string | The server's current name, as a confirmation. It must match exactly, including case; surrounding spaces are ignored. |

Example

```
curl -X DELETE "https://sillydev.co.uk/panel/api/client/servers/1a7ce997" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"name":"My Discord bot"}'
```

Response 204Done. There is no response body.

Errors

- 400 The request could not be carried out as sent. `detail` says why.
- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 422 Some fields were missing or invalid. There is one error per problem, and `meta.source_field` names the field.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### Get a server's resource use

GET`/servers/{server}/resources`

Returns the server's power state and current memory, CPU, disk and network use, as reported by its node. The answer is cached for up to 20 seconds (3 seconds while the server is booting); for a live feed use the websocket's `stats` events instead. Any server permission is enough to read it.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Example

```
curl "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/resources" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 200The server's resource use.

```
{
    "object": "stats",
    "attributes": {
        "current_state": "running",
        "is_suspended": false,
        "is_booting": false,
        "resources": {
            "memory_bytes": 83886080,
            "cpu_absolute": 1.25,
            "disk_bytes": 41943040,
            "network_rx_bytes": 102400,
            "network_tx_bytes": 51200,
            "uptime": 3600000
        }
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| objectrequired | string |  |
| attributesrequired | object |  |
| attributes.current_staterequired | one of 'offline', 'starting', 'running', 'stopping' |  |
| attributes.is_suspendedrequired | boolean |  |
| attributes.is_bootingrequired | boolean | True while the node is still loading the server after a restart. |
| attributes.resourcesrequired | object |  |
| attributes.resources.memory_bytesrequired | integer |  |
| attributes.resources.cpu_absoluterequired | number | CPU use as a percentage of one core, e.g. 150 is one and a half cores. |
| attributes.resources.disk_bytesrequired | integer |  |
| attributes.resources.network_rx_bytesrequired | integer | Bytes received since the server started. |
| attributes.resources.network_tx_bytesrequired | integer | Bytes sent since the server started. |
| attributes.resources.uptimerequired | integer | Milliseconds since the server started. 0 when offline. |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### List a server's activity

GET`/servers/{server}/activity`

Permission activity.read

Lists what has been done to the server and by whom: power actions, commands, file changes, settings changes and so on, 25 a page by default. Actions taken by administrators are left out. IP addresses are only shown for your own actions.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |
| page | integer | The page of results to return, starting at 1. |
| per_page | integer | How many entries to return on each page. Values above 100 are treated as 100. |
| sort | one of 'timestamp', '-timestamp' | `-timestamp` for newest first or `timestamp` for oldest first. Without it the order is not guaranteed. |
| filter\[event\] | string | Only entries whose event name contains this text, e.g. `server:power`. |
| include | one of 'actor' | `actor` adds the user who did each thing as `relationships.actor`. |

Example

```
curl "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/activity" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 200A page of activity entries.

```
{
    "object": "list",
    "data": [
        {
            "object": "activity_log",
            "attributes": {
                "id": "3f786850e387550fdab836ed7e6dc881de23001b",
                "batch": null,
                "event": "server:power.start",
                "is_api": true,
                "ip": "198.51.100.7",
                "description": null,
                "properties": [],
                "has_additional_metadata": false,
                "timestamp": "2026-10-10T12:00:00+00:00",
                "relationships": {
                    "actor": {
                        "object": "user",
                        "attributes": {
                            "uuid": "0b9c4e2a-6d1f-4f8a-9c3e-5a7b2d1e8f60",
                            "identifier": "user_BFZ4QKTPNVHU7HUYLUOHRH4QMA",
                            "username": "you",
                            "email": "you@example.com",
                            "image": "https://gravatar.com/avatar/00000000000000000000000000000000",
                            "2fa_enabled": true,
                            "created_at": "2026-01-15T09:30:00+00:00"
                        }
                    }
                }
            }
        }
    ],
    "meta": {
        "pagination": {
            "total": 1,
            "count": 1,
            "per_page": 25,
            "current_page": 1,
            "total_pages": 1,
            "links": []
        }
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| objectrequired | string |  |
| datarequired | array of object |  |
| data\[\].objectrequired | string |  |
| data\[\].attributesrequired | object | One thing that was done, by whom and when. |
| data\[\].attributes.idrequired | string | A stable unique key for the entry (a hash, not a database ID). |
| data\[\].attributes.batchrequired | string (uuid) \| null | Shared by entries logged together as part of one action. |
| data\[\].attributes.eventrequired | string | The event name, e.g. `server:power.start` or `server:file.write`. |
| data\[\].attributes.is_apirequired | boolean | Whether it was done with an API key. |
| data\[\].attributes.iprequired | string \| null | The IP address it came from. Only shown for your own actions (or to administrators). |
| data\[\].attributes.descriptionrequired | string \| null |  |
| data\[\].attributes.propertiesrequired | object | Details of the event, which vary by event. List values also get a `<key>_count` entry (or `count`, if there is only one list). An `ip` property is `[hidden]` unless it was your action. |
| data\[\].attributes.has_additional_metadatarequired | boolean | Whether `properties` holds anything beyond what the event's description already says. |
| data\[\].attributes.timestamprequired | string (date-time) |  |
| data\[\].attributes.relationships | object |  |
| data\[\].attributes.relationships.actor | any | Who did it. Only with `include=actor`; a `null_resource` if it was not a user (e.g. the system or a schedule). |
| metarequired | object |  |
| meta.paginationrequired | object |  |
| meta.pagination.total | integer |  |
| meta.pagination.count | integer |  |
| meta.pagination.per_page | integer |  |
| meta.pagination.current_page | integer |  |
| meta.pagination.total_pages | integer |  |
| meta.pagination.links | object |  |
| meta.pagination.links.previous | string (uri) |  |
| meta.pagination.links.next | string (uri) |  |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

## Power and console

Start, stop, restart and kill a server, send console commands, share console output, and get a token for the live console websocket.

### Get console websocket credentials

GET`/servers/{server}/websocket`

Permission websocket.connect Limited to 5 per minute per server (shared by all users of the server)

Returns a short-lived token and the URL of the server's live console websocket, which streams console output, status changes and resource use, and accepts commands and power actions. The token lasts 10 minutes and carries your permissions on the server at the time it was issued.

**Connecting.** Open a websocket to `data.socket`. The node checks the `Origin` header, so clients outside a browser must send `Origin: https://sillydev.co.uk`. Every message in both directions is a JSON text frame `{"event": "<name>", "args": ["..."]}`, where `args` is always a list of strings (and may be left out). First send `{"event": "auth", "args": ["<token>"]}`. The node answers `auth success`, then sends the current `status` (and, if the server is offline, a `stats` message). Nothing else is sent or accepted until you have authenticated.

**Main events.** Send `send logs` to get recent console history (only while the server is running; each line arrives as a `console output` event), `send command` with the command as `args[0]` (needs `control.console`, ignored while the server is offline), `set state` with `start`, `stop`, `restart` or `kill` as `args[0]` (needs the matching `control.*` permission), and `send stats` for an immediate `stats` message. You receive `console output` (one line per message), `status` (`offline`, `starting`, `running` or `stopping`), `stats` (whose `args[0]` is a JSON string with `memory_bytes`, `memory_limit_bytes`, `cpu_absolute`, `network.rx_bytes`, `network.tx_bytes`, `uptime` in milliseconds, `state` and `disk_bytes`), and `daemon message` (notes from the node, e.g. about installation).

**Keeping the token fresh.** About a minute before the token expires the node sends `token expiring`, and `token expired` once it has. Call this endpoint again and send a new `auth` message on the same connection; there is no need to reconnect. A problem with the token arrives as `jwt error`, other failures as `daemon error`, and sending too fast as `throttled` (with the event name as `args[0]`). The node limits `auth` and `send logs` to 2 every 5 seconds, `send command` to 10 a second and other events to 4 a second.

**Close codes.** The node closes the connection with code 4409 if the server is suspended (do not reconnect) and 4410 while it is still loading the server after a node restart (wait a few seconds and connect again).

This endpoint itself is limited to 5 requests a minute per server, shared by everyone using that server.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Messages

| Name | Type | Description |
| --- | --- | --- |
| auth | you send | Authenticate, or re-authenticate with a fresh token. `args[0]` is the token from this endpoint. |
| send logs | you send | Ask for recent console history. Lines arrive as `console output` events. Ignored while the server is not running. |
| send command | you send | Type a command into the console. `args[0]` is the command. Needs `control.console`; ignored while the server is offline. |
| set state | you send | Send a power action. `args[0]` is `start`, `stop`, `restart` or `kill`. Needs `control.start`, `control.stop` (also for `kill`) or `control.restart`. |
| send stats | you send | Ask for a `stats` message straight away. |
| auth success | you receive | The token was accepted. |
| console output | you receive | One line of console output in `args[0]`. |
| status | you receive | The server's power state changed. `args[0]` is `offline`, `starting`, `running` or `stopping`. |
| stats | you receive | Resource use. `args[0]` is a JSON string with `memory_bytes`, `memory_limit_bytes`, `cpu_absolute`, `network` (`rx_bytes`, `tx_bytes`), `uptime` (milliseconds), `state` and `disk_bytes`. |
| daemon message | you receive | A message from the node rather than the server's own output, such as installation progress. |
| install started | you receive | The server's install script has started. |
| install output | you receive | One line of install script output. Only sent to administrators. |
| install completed | you receive | The server's install script has finished. |
| backup completed | you receive | A backup finished. The event name is `backup completed:<backup uuid>` and `args[0]` is a JSON string describing the result. Needs `backup.read`. |
| backup restore completed | you receive | Restoring a backup finished. |
| transfer status | you receive | The status of a transfer of the server to another node. |
| transfer logs | you receive | Transfer log output. Only sent to administrators. |
| token expiring | you receive | The token expires within about a minute. Get a new one from this endpoint and send `auth` again. |
| token expired | you receive | The token has expired. Get a new one and send `auth` again. |
| jwt error | you receive | The token is missing, expired, for another server or otherwise invalid. `args[0]` says why. |
| daemon error | you receive | Handling a message failed. `args[0]` is an error message with a reference ID. |
| throttled | you receive | Messages of the event named in `args[0]` are being dropped because they were sent too fast. |

Example

```
curl "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/websocket" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 200A websocket token and address.

```
{
    "data": {
        "token": "eyJhbGciOi...",
        "socket": "wss://lon3.sillydev.co.uk:8080/api/servers/1a7ce997-259b-452e-8b4e-cecc464142ca/ws"
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| datarequired | object |  |
| data.tokenrequired | string | A signed token valid for 10 minutes. Send it in the `auth` event. |
| data.socketrequired | string (uri) | The websocket URL (`wss://`) on the server's node. |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### Send a console command

POST`/servers/{server}/command`

Permission control.console

Types a command into the server's console, as if entered in the panel. The server must be running: if it is not, the request fails with 502. The command's output is not returned; read it from the websocket.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Body

| Name | Type | Description |
| --- | --- | --- |
| commandrequired | string | The command, without a trailing newline. |

Example

```
curl -X POST "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/command" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"command":"reload"}'
```

Response 204Done. There is no response body.

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 422 Some fields were missing or invalid. There is one error per problem, and `meta.source_field` names the field.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.
- 502 The server is not running, or its node could not be reached.

### Share console output

POST`/servers/{server}/logs/share`

Permission websocket.connect Limited to 6 per minute per user

Uploads console text you send to mclo.gs, a public paste site, and returns its link. Email addresses, tokens, passwords, credentials in URLs, IP addresses and private keys are replaced with placeholders such as `[token]` first, but check before sharing the link: the paste is public. Text over mclo.gs's limits (25,000 lines or 10 MiB) is cut from the start, keeping the latest output.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Body

| Name | Type | Description |
| --- | --- | --- |
| contentrequired | string | The console text to share. The panel does not fetch it from the server; you send it. |

Example

```
curl -X POST "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/logs/share" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"content":"Logged in as My Discord bot#1234\nError: Missing Access\n    at RequestHandler.execute"}'
```

Response 200The public link to the shared log.

```
{
    "url": "https://mclo.gs/aB3dE5f"
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| urlrequired | string (uri) |  |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 422 Some fields were missing or invalid. There is one error per problem, and `meta.source_field` names the field.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.
- 502 mclo.gs could not take the log. Try again in a moment.

### Start, stop, restart or kill a server

POST`/servers/{server}/power`

Permission control.start control.stop control.restart

Sends a power action to the server. The node accepts it straight away and carries it out in the background, so a 204 means the action was accepted, not that it has finished: watch `status` on the websocket or poll the resources endpoint.

- `start` starts the server if it is stopped. - `stop` stops it gracefully with the egg's stop command, and also cancels a start that has not yet started the container. - `restart` stops it gracefully and starts it again (or just starts it if stopped). - `kill` stops the container immediately, which can lose unsaved data. It skips the queue below and cancels any start or restart in progress.

A server runs one power action at a time; one sent while another is running waits, and only the most recent waiting action is kept. Starts also wait their turn in the node's start queue when many servers are starting at once (paid servers go first), and the console says so while they wait. Suspended servers cannot be started.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Body

| Name | Type | Description |
| --- | --- | --- |
| signalrequired | one of 'start', 'stop', 'restart', 'kill' |  |

Example

```
curl -X POST "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/power" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"signal":"restart"}'
```

Response 204Done. There is no response body.

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 422 Some fields were missing or invalid. There is one error per problem, and `meta.source_field` names the field.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

## Settings

Rename a server, reinstall it, and change the Docker image it runs on.

### Rename a server

POST`/servers/{server}/settings/rename`

Permission settings.rename

Changes the server's name and, optionally, its description. Leave `description` out to keep the current one; send `null` or an empty string to clear it. On a paid server the new name is also shown on invoices.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Body

| Name | Type | Description |
| --- | --- | --- |
| namerequired | string |  |
| description | string \| null |  |

Example

```
curl -X POST "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/settings/rename" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"name":"My Discord bot","description":"Moderation bot for my server"}'
```

Response 204Done. There is no response body.

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 422 Some fields were missing or invalid. There is one error per problem, and `meta.source_field` names the field.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### Reinstall a server

POST`/servers/{server}/settings/reinstall`

Permission settings.reinstall

Stops the server and runs its install script again. The server's `status` becomes `installing` until the script finishes, and most endpoints answer 409 meanwhile. This is also the one thing you can still do to a server whose install failed (`status` `install_failed`). Servers set to skip install scripts cannot be reinstalled (400) unless their install failed.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Example

```
curl -X POST "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/settings/reinstall" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 202The reinstall has started. The body is an empty JSON array.

```
[]
```

Errors

- 400 The request could not be carried out as sent. `detail` says why.
- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### Change the Docker image

PUT`/servers/{server}/settings/docker-image`

Permission startup.docker-image

Switches the server to another of the Docker images its egg offers, for example a newer Node.js or Python version. The list of choices is `meta.docker_images` from the startup endpoint. It takes effect the next time the server starts. If an administrator set a custom image, it cannot be changed here (400).

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Body

| Name | Type | Description |
| --- | --- | --- |
| docker_imagerequired | string | One of the image values (not the display names) in the startup endpoint's `meta.docker_images`. |

Example

```
curl -X PUT "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/settings/docker-image" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"docker_image":"ghcr.io/pterodactyl/yolks:nodejs_22"}'
```

Response 204Done. There is no response body.

Errors

- 400 The request could not be carried out as sent. `detail` says why.
- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 422 Some fields were missing or invalid. There is one error per problem, and `meta.source_field` names the field.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### Get the status badge

GET`/servers/{server}/settings/badge`

Permission settings.rename

Whether the server's public status badge is on, and if so its image address and a Markdown snippet for a README. The badge is an image that says `online`, `starting`, `offline` or `unknown`, refreshed at most once a minute, and nothing else about the server. Owner only, with a key that has `settings.rename`; subusers cannot see or change it.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Example

```
curl "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/settings/badge" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 200The badge's settings. \`url\` and \`markdown\` are null while it is off.

```
{
    "object": "server_badge",
    "attributes": {
        "enabled": true,
        "show_name": false,
        "url": "https://sillydev.co.uk/badge/k3Q9vX2mL7pR4tW8zB1nC6dF0gH5jS2aE9uY3iO7.svg",
        "link": "https://sillydev.co.uk/?ref=badge",
        "markdown": "[![bot status](https://sillydev.co.uk/badge/k3Q9vX2mL7pR4tW8zB1nC6dF0gH5jS2aE9uY3iO7.svg)](https://sillydev.co.uk/?ref=badge)"
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| objectrequired | string |  |
| attributesrequired | object |  |
| attributes.enabledrequired | boolean | Whether the badge is on. |
| attributes.show_namerequired | boolean | Whether the badge shows the server's name rather than "bot". |
| attributes.urlrequired | string \| null | The badge image's address, or null while it is off. Anyone with it can see the server's state, so share it only where you mean to. |
| attributes.linkrequired | string | Where the badge should link to. |
| attributes.markdownrequired | string \| null | A Markdown snippet for a README, or null while the badge is off. |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### Turn on the status badge

PUT`/servers/{server}/settings/badge`

Permission settings.rename Limited to 20 per minute

Turns the badge on, giving it a new random address (201), or changes whether it shows the server's name (200). Off by default; until it is turned on there is no badge to find. With `show_name` false or left out the badge's label is just "bot". Owner only, with a key that has `settings.rename`.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Body

| Name | Type | Description |
| --- | --- | --- |
| show_name | boolean | Show the server's name on the badge instead of "bot". Left out, the current setting is kept (off for a new badge). |

Example

```
curl -X PUT "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/settings/badge" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"show_name":false}'
```

Response 201The badge is now on, at a new address.

```
{
    "object": "server_badge",
    "attributes": {
        "enabled": true,
        "show_name": false,
        "url": "https://sillydev.co.uk/badge/k3Q9vX2mL7pR4tW8zB1nC6dF0gH5jS2aE9uY3iO7.svg",
        "link": "https://sillydev.co.uk/?ref=badge",
        "markdown": "[![bot status](https://sillydev.co.uk/badge/k3Q9vX2mL7pR4tW8zB1nC6dF0gH5jS2aE9uY3iO7.svg)](https://sillydev.co.uk/?ref=badge)"
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| objectrequired | string |  |
| attributesrequired | object |  |
| attributes.enabledrequired | boolean | Whether the badge is on. |
| attributes.show_namerequired | boolean | Whether the badge shows the server's name rather than "bot". |
| attributes.urlrequired | string \| null | The badge image's address, or null while it is off. Anyone with it can see the server's state, so share it only where you mean to. |
| attributes.linkrequired | string | Where the badge should link to. |
| attributes.markdownrequired | string \| null | A Markdown snippet for a README, or null while the badge is off. |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 422 Some fields were missing or invalid. There is one error per problem, and `meta.source_field` names the field.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### Turn off the status badge

DELETE`/servers/{server}/settings/badge`

Permission settings.rename

Turns the badge off and forgets its address, which then shows `unknown` with a 404 (copies cached elsewhere can last up to a minute). Turning it on again makes a new address. Owner only, with a key that has `settings.rename`.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Example

```
curl -X DELETE "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/settings/badge" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 204Done. There is no response body.

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### Replace the status badge's address

POST`/servers/{server}/settings/badge/regenerate`

Permission settings.rename Limited to 10 per minute

Gives the badge a new random address. The old one stops working at once, though copies cached elsewhere can last up to a minute, so update your README. 404 while the badge is off. Owner only, with a key that has `settings.rename`.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Example

```
curl -X POST "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/settings/badge/regenerate" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 200The badge, at its new address.

```
{
    "object": "server_badge",
    "attributes": {
        "enabled": true,
        "show_name": false,
        "url": "https://sillydev.co.uk/badge/Zt5Yh2Kq8Wm3Np6Rs9Vb1Xc4Df7Gj0Lk2Pa5Qe8U.svg",
        "link": "https://sillydev.co.uk/?ref=badge",
        "markdown": "[![bot status](https://sillydev.co.uk/badge/Zt5Yh2Kq8Wm3Np6Rs9Vb1Xc4Df7Gj0Lk2Pa5Qe8U.svg)](https://sillydev.co.uk/?ref=badge)"
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| objectrequired | string |  |
| attributesrequired | object |  |
| attributes.enabledrequired | boolean | Whether the badge is on. |
| attributes.show_namerequired | boolean | Whether the badge shows the server's name rather than "bot". |
| attributes.urlrequired | string \| null | The badge image's address, or null while it is off. Anyone with it can see the server's state, so share it only where you mean to. |
| attributes.linkrequired | string | Where the badge should link to. |
| attributes.markdownrequired | string \| null | A Markdown snippet for a README, or null while the badge is off. |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

## Startup

The startup command and the startup variables a server runs with.

### Get startup settings

GET`/servers/{server}/startup`

Permission startup.read

Returns the startup variables you can see, the startup command with their values filled in, and the Docker images you can choose from. If a linked repository's `sillydev.conf` also sets some of these, `meta.sillydev` names them and says whether a change made here is written back to the file or will be undone by the next deploy.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Example

```
curl "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/startup" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json"
```

Response 200The server's startup variables and command.

```
{
    "object": "list",
    "data": [
        {
            "object": "egg_variable",
            "attributes": {
                "name": "Main file",
                "description": "The file the bot is started from.",
                "env_variable": "MAIN_FILE",
                "default_value": "index.js",
                "server_value": "bot.js",
                "is_editable": true,
                "rules": "required|string|max:64"
            }
        }
    ],
    "meta": {
        "startup_command": "node /home/container/bot.js",
        "raw_startup_command": "node /home/container/{{MAIN_FILE}}",
        "docker_images": {
            "Node.js 20": "ghcr.io/pterodactyl/yolks:nodejs_20",
            "Node.js 22": "ghcr.io/pterodactyl/yolks:nodejs_22"
        },
        "sillydev": null
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| objectrequired | string |  |
| datarequired | array of object |  |
| data\[\].objectrequired | string |  |
| data\[\].attributesrequired | object | A startup variable of the server's egg. |
| data\[\].attributes.namerequired | string |  |
| data\[\].attributes.descriptionrequired | string \| null |  |
| data\[\].attributes.env_variablerequired | string | The environment variable name, used as `key` when changing it. |
| data\[\].attributes.default_valuerequired | string \| null |  |
| data\[\].attributes.server_valuerequired | string \| null | This server's value. |
| data\[\].attributes.is_editablerequired | boolean | Whether you may change it. |
| data\[\].attributes.rulesrequired | string | Laravel validation rules the value must satisfy, e.g. `required\|string\|max:64`. |
| metarequired | object |  |
| meta.startup_commandrequired | string | The startup command with variable values filled in. |
| meta.raw_startup_commandrequired | string | The startup command with `{{VARIABLE}}` placeholders. |
| meta.docker_imagesrequired | object | The images you can switch to, as display name to image. |
| meta.sillydevrequired | any | Set when the linked repository's sillydev.conf also sets some of these settings, otherwise null. |

Errors

- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.

### Change a startup variable

PUT`/servers/{server}/startup/variable`

Permission startup.update

Sets one startup variable and returns it with the updated startup command. The value is checked against the variable's own `rules`. Variables you cannot see or that are read-only (`is_editable` false) are refused with 400. The change takes effect the next time the server starts, and is written to the linked repository's sillydev.conf too if that file sets this variable and write-back is on.

Parameters

| Name | Type | Description |
| --- | --- | --- |
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |

Body

| Name | Type | Description |
| --- | --- | --- |
| keyrequired | string | The variable's `env_variable`, e.g. `MAIN_FILE`. |
| valuerequired | string \| null | The new value. It must be present; whether it may be null or empty depends on the variable's rules. Null is stored as an empty string. |

Example

```
curl -X PUT "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/startup/variable" \
  -H "Authorization: Bearer $SILLYDEV_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"key":"MAIN_FILE","value":"bot.js"}'
```

Response 200The updated variable.

```
{
    "object": "egg_variable",
    "attributes": {
        "name": "Main file",
        "description": "The file the bot is started from.",
        "env_variable": "MAIN_FILE",
        "default_value": "index.js",
        "server_value": "bot.js",
        "is_editable": true,
        "rules": "required|string|max:64"
    },
    "meta": {
        "startup_command": "node /home/container/bot.js",
        "raw_startup_command": "node /home/container/{{MAIN_FILE}}"
    }
}
```

**Every field in the response**

| Name | Type | Description |
| --- | --- | --- |
| objectrequired | string |  |
| attributesrequired | object | A startup variable of the server's egg. |
| attributes.namerequired | string |  |
| attributes.descriptionrequired | string \| null |  |
| attributes.env_variablerequired | string | The environment variable name, used as `key` when changing it. |
| attributes.default_valuerequired | string \| null |  |
| attributes.server_valuerequired | string \| null | This server's value. |
| attributes.is_editablerequired | boolean | Whether you may change it. |
| attributes.rulesrequired | string | Laravel validation rules the value must satisfy, e.g. `required\|string\|max:64`. |
| metarequired | object |  |
| meta.startup_commandrequired | string |  |
| meta.raw_startup_commandrequired | string |  |

Errors

- 400 The request could not be carried out as sent. `detail` says why.
- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 422 Some fields were missing or invalid. There is one error per problem, and `meta.source_field` names the field.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.
