Every API here is available over the APIs.io API and to AI agents over MCP.
{
"openapi": "3.0.0",
"info": {
"title": "GenHTTP Lambda API",
"description": "GenHTTP Lambda hosts small C# web applications. A **lambda** is a snippet of C#\nthat returns a [GenHTTP](https://genhttp.org/) handler - a page, an API, a\nwebsite, a websocket - plus whatever other files it needs. The platform compiles\nit and serves it at an address of its own, `https://{publicKey}.{hosting domain}/`.\nThis API is everything the editor does: create a lambda, save its versions, put\none online, look at its data, its traffic and its logs.\n\n## Getting started\n\n```bash\n# 1. create a lambda - keep its privateKey,\n# which cannot be recovered\ncurl -X POST {server}/lambdas \\\n -H 'Content-Type: application/json' \\\n -d '{ \"acceptedTerms\": true }'\n\n# 2. save its code as a version, and put\n# it online in the same call\ncurl -X POST \\\n '{server}/lambdas/{privateKey}/versions?deploy=true' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"files\": [ {\n \"name\": \"lambda.cs\",\n \"code\": \"return Inline.Create().Get(() => \\\"Hi\\\");\"\n } ],\n \"change\": \"Says hi\"\n }'\n\n# 3. it answers at the address the answer\n# names (deployment.lambda.address)\n```\n\nThe terms that step 1 accepts are in `GET /system`, along with this\ninstallation's limits and the address template (`lambdaUrl`). The demos\n(`GET /demos`) are finished lambdas to read, and to start from: pass a demo's\n`id` as the `template` when creating a lambda.\n\n## Who may do what\n\nThere are no accounts. A lambda has two keys:\n\n- The **public key** names its address. It is not a secret, and its owner may\n change it.\n- The **private key** (the *editor key*) is in the path of every call that reads\n or changes the lambda: `/lambdas/{privateKey}/…`. Holding it is the\n authorization. Whoever has it may change, deploy and delete the lambda, so treat\n it like a password: do not log it or put it in a URL a third party sees. It is\n handed out once, to whoever creates the lambda, and cannot be recovered.\n\nA key that names no lambda is answered with `404`, the same way as one whose\nlambda was deleted. The demos' editor keys are public. A demo can be read like\nany other lambda, but every change is refused with `403`.\n\nThe operations under *Operating the installation* are for whoever runs the\nserver. They take the token of the installation in the `X-Admin-Token` header:\n`401` without it, `403` with a wrong one. An installation without a token does not\nhave them at all (`404`).\n\n## What a lambda is made of\n\n- A **version** is the program: its code and its resources. It never changes\n once it is saved; a change is a new version. Saving one does not put it online,\n deploying does (`?deploy=true` on a save, or `POST …/deployment/start`).\n A new lambda starts with version 1, which is the template it was made from,\n and nothing is online until a version is deployed. Only so many versions are\n kept (see the limits); the oldest go first, but never the version that is\n online or one a feature is based on.\n- The **data** is what the program keeps: its **database** (SQLite), its\n **workspace** (files it reads and writes) and its **secrets** (API keys,\n passwords). Data belongs to the lambda and is shared by every version.\n Deploying, rolling back and merging never touch it. Each kind of data is\n switched on before the lambda can use it (`PUT …/data/{kind}`). Switching a\n kind off deletes everything it held.\n- A **feature** is a change worked on beside the lambda. It starts as a copy of\n a version and of the data, is saved in place as often as needed, has a preview\n at its own address, and becomes the next version when it is merged. Only a\n feature based on the newest version can be merged.\n\n## Files\n\nA version is sent and read as a list of files:\n\n```json\n{ \"name\": \"lambda.cs\", \"code\": \"return Inline.Create()…;\" }\n{ \"name\": \"resources/web/logo.png\", \"code\": \"iVBORw0KGgo…\", \"encoding\": \"base64\" }\n```\n\n- `lambda.cs` comes first. It is the snippet that returns the handler.\n- Every other `.cs` file, in any folder, is compiled with it.\n- Files below `resources/` are the **resources**: what the lambda reads and\n serves while it runs (`Resources.App(\"web\")` serves `resources/web/`).\n- Every other file - `docs/product.md`, `tests/README.md`, the sources of a front\n end - is kept with the version and never compiled or served.\n- Text is sent as it is. Anything else is sent base64 encoded with\n `\"encoding\": \"base64\"`. C# files are always text.\n- Names are case sensitive, but two names that differ only by their case are\n refused, as are names that would not survive a clone or a zip on Windows. A\n refused save says which name it refused and why.\n\nA save that only changes some files does not have to send all of them:\n`POST …/versions/changes` and `POST …/features/{feature}/changes` add, replace and\nremove files, or replace text inside them.\n\n## Limits\n\nWhat a version may come to (its code and resources together), the room its data\nmay take (database and workspace together), and how many versions and features\nare kept are set by the operator of the installation, per tier, and may change.\nRead them instead of assuming them: `GET /system` for a new lambda, and the\n`limits` of `GET /lambdas/{privateKey}/summary` for an existing one. A save or\nan upload that does not fit is refused with `400`, and a feature or a secret\nmore than a lambda may have with `409`. The message names the limit either\nway.\n\nThe tier of a lambda (`Free`, `Premium` or `Demo`) is assigned by the operator.\nA free lambda that nobody visits or edits for a while is taken offline, and\nlater removed. `deployedUntil` and `keptUntil` say when; both move forward with\nevery visit and every edit.\n\n## Answers and errors\n\nRequests and answers are JSON unless an operation says otherwise. Field names are\ncamelCase, times are ISO 8601 in UTC, and sizes are in bytes. Where a body is\noptional, or a field is left out of a `PATCH`, what is left out stays as it is.\n\nA request that is refused is answered with a status code and a JSON body:\n\n```json\n{\n \"status\": 409,\n \"error\": \"Conflict\",\n \"message\": \"This key is already in use.\"\n}\n```\n\n- `400` **Invalid**: the request cannot be carried out as it is. The message says\n what to change.\n- `403` **Forbidden**: not for this lambda - it is a demo, or its tier does not\n include what was asked for.\n- `404` **NotFound**: the lambda, version, feature, file or table does not exist.\n- `409` **Conflict**: it collides with the state of the lambda - a key or a domain\n that is taken, a feature that is behind the newest version, a save based on a\n state that changed in the meantime. Read again, then retry.\n- `429` **TooMany**: asked for too often lately. Try again later.\n\nThe `message` is written for a person or an agent to act on. Pass it on rather\nthan replacing it. Code that does not compile is not an error of the request:\ndeploying, previewing and merging answer it with `422` and the compiler's\n`diagnostics`, and leave what was online as it was.\n\nA request that fails before the platform's own checks - a body that is not valid\nJSON, a build agent that cannot be reached - is answered with the framework's\nerror body, which has `status` and `message` but no `error`.\n\n## Other ways in\n\nThe same platform is available to AI agents as an MCP server at `/mcp`. Every\nlambda is also a git repository: clone it from the `gitPath` of the lambda, push\nto `main` to save versions, and push a branch to start a feature.\n",
"version": "v1"
},
"servers": [
{
"url": "https://genhttp.dev/api/v1"
}
],
"paths": {
"/lambdas": {
"post": {
"tags": [
"Lambdas"
],
"summary": "Creates a lambda",
"description": "The new lambda is in the free tier and has one version: a copy of the `template`, or a snippet that says hello if\nnone is given. Nothing is online until a version is deployed (`POST /lambdas/{privateKey}/deployment/start`, or a\nsave with `?deploy=true`). A copy of a demo that keeps records starts with an empty database of its own, switched on;\nthe demo's records are not copied.\n\nThe answer holds the editor key (`privateKey`). It is handed out this once and cannot be recovered: whoever loses it\nloses the lambda, and whoever finds it may change it.\n\nA `publicKey` that is asked for is trimmed and lower cased, and must be 3 to 40 characters of `a-z`, `0-9` and `-`,\nneither starting nor ending with a dash. It may not start with `demo-` or `xn--`, and a few names are reserved for\nthe platform (`www`, `api`, `admin`, `mail` and the like). `GET /keys/{publicKey}` says whether a key can be claimed\nbefore trying. Left out, a random key of eight characters is chosen.",
"operationId": "lambdasPOST",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateLambdaRequest"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/CreateLambdaRequest"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/CreateLambdaRequest"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/CreateLambdaRequest"
}
},
"text/xml": {
"schema": {
"$ref": "#/components/schemas/CreateLambdaRequest"
}
}
}
},
"responses": {
"201": {
"description": "The lambda was created. Keep `privateKey`: it is not handed out again.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"text/xml": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
}
}
},
"400": {
"description": "The terms were not accepted, or the key, the template or the view cannot be used. The message says which.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"409": {
"description": "Another lambda has the public key that was asked for.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/lambdas/{privateKey}": {
"get": {
"tags": [
"Lambdas"
],
"summary": "Reads a lambda",
"description": "Its keys and tier, its newest version and the one online, until when it stays online and is kept, and where it answers.",
"operationId": "lambdasGET",
"parameters": [
{
"name": "privateKey",
"in": "path",
"required": true,
"description": "The editor key of the lambda. Holding it is what allows the call.",
"schema": {
"title": "String",
"type": "string"
}
}
],
"responses": {
"204": {
"description": "A response containing no body"
},
"200": {
"description": "The lambda",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"text/xml": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
}
}
},
"404": {
"description": "No lambda has this editor key. A key that never existed and the key of a deleted lambda are answered alike.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
},
"patch": {
"tags": [
"Lambdas"
],
"summary": "Changes the public key or the view of a lambda",
"description": "What the request leaves out stays as it is.\n\nA new `publicKey` follows the rules of `POST /lambdas` and moves the address the lambda answers at, at once: nothing\nanswers at the old address any more, and anybody may claim it. A domain of the lambda's own is not affected.\n\nThe `view` is only what the editor opens in for somebody who has not picked a view of their own.",
"operationId": "lambdasPATCH",
"parameters": [
{
"name": "privateKey",
"in": "path",
"required": true,
"description": "The editor key of the lambda. Holding it is what allows the call.",
"schema": {
"title": "String",
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateLambdaRequest"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/UpdateLambdaRequest"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/UpdateLambdaRequest"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/UpdateLambdaRequest"
}
},
"text/xml": {
"schema": {
"$ref": "#/components/schemas/UpdateLambdaRequest"
}
}
}
},
"responses": {
"204": {
"description": "A response containing no body"
},
"200": {
"description": "The lambda afterwards",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
},
"text/xml": {
"schema": {
"$ref": "#/components/schemas/LambdaResponse"
}
}
}
},
"400": {
"description": "The key or the view cannot be used. Nothing was changed.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"409": {
"description": "Another lambda has the public key that was asked for.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"403": {
"description": "The lambda is a demo, which anybody may read and nobody may change. Create a lambda of your own from it instead: `POST /lambdas` with the demo's id as `template`.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"404": {
"description": "No lambda has this editor key. A key that never existed and the key of a deleted lambda are answered alike.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
},
"delete": {
"tags": [
"Lambdas"
],
"summary": "Deletes a lambda for good",
"description": "Takes it offline and removes everything kept for it: every version, every feature with its preview, its database,\nworkspace and secrets, its showcase entry and published source, and what was counted of its visitors. This cannot be\nundone, and its public key is free for anybody to claim afterwards. `GET …/export` takes a copy first.",
"operationId": "lambdasDELETE",
"parameters": [
{
"name": "privateKey",
"in": "path",
"required": true,
"description": "The editor key of the lambda. Holding it is what allows the call.",
"schema": {
"title": "String",
"type": "string"
}
}
],
"responses": {
"204": {
"description": "The lambda and everything kept for it are gone."
},
"403": {
"description": "The lambda is a demo, which anybody may read and nobody may change. Create a lambda of your own from it instead: `POST /lambdas` with the demo's id as `template`.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"404": {
"description": "No lambda has this editor key. A key that never existed and the key of a deleted lambda are answered alike.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/lambdas/{privateKey}/export": {
"get": {
"tags": [
"Lambdas"
],
"summary": "Exports the lambda as a .NET project",
"description": "A zip of the **newest** version - not necessarily the one online - as a .NET 10 project on GenHTTP: a `Program.cs`\nthat hosts the snippet, stand-ins for what the platform provides in `Platform/`, and a `Dockerfile`. `dotnet run` or\n`docker build` it, and it serves what the lambda serves.\n\nThe database comes along as `database/database.db`, a copy taken while the request is answered, which the project\nopens as the lambda opened it here. The workspace does not come along, and neither do the values of the secrets: the\nproject reads them from environment variables of the same names, which `Program.cs` lists. A published source\ncarries its `LICENSE`. Exporting changes nothing about the lambda.",
"operationId": "export",
"parameters": [
{
"name": "privateKey",
"in": "path",
"required": true,
"description": "The editor key of the lambda. Holding it is what allows the call.",
"schema": {
"title": "String",
"type": "string"
}
}
],
"responses": {
"200": {
"description": "The project, as a zip archive",
"headers": {
"Content-Disposition": {
"description": "`attachment`, named after the public key: `quiz.zip`",
"schema": {
"title": "String",
"type": "string"
}
}
},
"content": {
"application/zip": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
},
"404": {
"description": "No lambda has this editor key, or it has no version to export.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/lambdas/{privateKey}/versions": {
"get": {
"tags": [
"Versions"
],
"summary": "Lists the versions that are kept, newest first",
"description": "Without their files. `GET …/versions/{version}` reads one with them.",
"operationId": "versionsAll",
"parameters": [
{
"name": "privateKey",
"in": "path",
"required": true,
"description": "The editor key of the lambda. Holding it is what allows the call.",
"schema": {
"title": "String",
"type": "string"
}
}
],
"responses": {
"204": {
"description": "A response containing no body"
},
"200": {
"description": "The versions, newest first",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/VersionResponse"
}
}
},
"application/yaml": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/VersionResponse"
}
}
},
"application/x-www-form-urlencoded": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/VersionResponse"
}
}
},
"application/xml": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/VersionResponse"
}
}
},
"text/xml": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/VersionResponse"
}
}
}
}
},
"404": {
"description": "No lambda has this editor key. A key that never existed and the key of a deleted lambda are answered alike.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
},
"post": {
"tags": [
"Versions"
],
"summary": "Saves every file of the lambda as a new version",
"description": "`files` is the whole lambda, `lambda.cs` first: a file the newest version has and this request does not is not in\nthe new version. To change a few files, `POST …/versions/changes` sends only those.\n\nA file name may be refused - a C# file has to be named as C# names its files, a resource needs an extension, and\nno name may hold spaces or be one Windows reserves; the message says what is wrong with which. The files together\nmay not come to more than the tier of the lambda allows a version (see the limits). Whether the code compiles is\nnot checked unless it is deployed: `POST …/code/check` compiles it without saving.",
"operationId": "versionsPOST",
"parameters": [
{
"name": "privateKey",
"in": "path",
"required": true,
"description": "The editor key of the lambda. Holding it is what allows the call.",
"schema": {
"title": "String",
"type": "string"
}
},
{
"name": "deploy",
"in": "query",
"description": "Whether to put the new version online as well. The version is saved either way; whether it went online is in `deployment`.",
"schema": {
"title": "Boolean",
"type": "boolean"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/VersionRequest"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/VersionRequest"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/VersionRequest"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/VersionRequest"
}
},
"text/xml": {
"schema": {
"$ref": "#/components/schemas/VersionRequest"
}
}
}
},
"responses": {
"201": {
"description": "The version was saved. If it was to be deployed as well, `deployment` says whether that worked.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SavedVersionResponse"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/SavedVersionResponse"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/SavedVersionResponse"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/SavedVersionResponse"
}
},
"text/xml": {
"schema": {
"$ref": "#/components/schemas/SavedVersionResponse"
}
}
}
},
"400": {
"description": "There are no files, `lambda.cs` is not the first, a name or an encoding cannot be used, or the files are larger than a version may be. Nothing was saved.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"403": {
"description": "The lambda is a demo, which anybody may read and nobody may change. Create a lambda of your own from it instead: `POST /lambdas` with the demo's id as `template`.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"404": {
"description": "No lambda has this editor key. A key that never existed and the key of a deleted lambda are answered alike.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/lambdas/{privateKey}/versions/{version}": {
"get": {
"tags": [
"Versions"
],
"summary": "Reads a version with its files",
"description": "Every file, `lambda.cs` first, unless a `folder` is named: `?folder=docs/` reads the documentation of the version\nand `?folder=tests/` its tests, without every resource coming along.",
"operationId": "versionsGET",
"parameters": [
{
"name": "privateKey",
"in": "path",
"required": true,
"description": "The editor key of the lambda. Holding it is what allows the call.",
"schema": {
"title": "String",
"type": "string"
}
},
{
"name": "version",
"in": "path",
"required": true,
"description": "The number of the version, counting from 1",
"schema": {
"title": "Integer",
"type": "integer",
"format": "int32"
}
},
{
"name": "folder",
"in": "query",
"description": "Only the files whose name starts with this, such as `docs/` or `resources/`",
"schema": {
"title": "String",
"type": "string"
}
}
],
"responses": {
"204": {
"description": "A response containing no body"
},
"200": {
"description": "The version with its files",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/VersionContentResponse"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/VersionContentResponse"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/VersionContentResponse"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/VersionContentResponse"
}
},
"text/xml": {
"schema": {
"$ref": "#/components/schemas/VersionContentResponse"
}
}
}
},
"404": {
"description": "No lambda has this editor key, or it has no such version - it never existed, or it was not kept.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/lambdas/{privateKey}/versions/{version}/zip": {
"get": {
"tags": [
"Versions"
],
"summary": "Downloads a version as a zip archive",
"description": "The archive holds the files as the lambda names them, so it can be changed locally and saved again as a new version\nwith `POST …/versions/zip`. With `?layout=project` it holds them where a clone of the lambda's git repository has\nthem instead, so a .NET tool set up in a clone finds them where it looks. Unlike the export, it holds no project\naround them and no data.",
"operationId": "zipGET",
"parameters": [
{
# --- truncated at 32 KB (492 KB total) ---
# Full source: https://artifacts.apis.io/genhttp-dev/openapi/_original/genhttp-dev-v1-openapi.json