# Applications

An application is one deployed project. Creating one also creates its production [environment](/reference/api/environments), so a single call gets you a live URL.

With an [API key](/reference/api/authentication) the organisation is inferred. Session tokens must add `targetOrganisationId` to every body.

## List applications

```endpoint
POST /api/applications
> Applications in the organisation, newest first, paginated.
permission: read:projects

param page | number | 1 | 1-based page number
param limit | number | 10 | Between 1 and 100
param filter | string | optional | Free text, matched against the name
param sortColumn | string | created_at | Field to order by
param sortOrder | string | desc | asc or desc
param targetOrganisationId | string | session only | Required when using a session token

response 200 | Paginated envelope
{
  "items": [
    {
      "id": "app_4kQ2",
      "name": "shop",
      "slug": "shop",
      "deployment_type": "container",
      "framework": "nextjs",
      "runtime": "nodejs",
      "github_repo_url": "https://github.com/acme/shop",
      "github_branch": "main",
      "source_type": "github",
      "status": "healthy",
      "url": "https://main-shop-acme.light-cloud.io",
      "created_at": "2026-08-01T09:14:00.000Z",
      "updated_at": "2026-08-28T07:02:00.000Z"
    }
  ],
  "totalItems": 1,
  "totalPages": 1,
  "currentPage": 1
}

code curl
curl -X POST https://api.light-cloud.com/api/applications \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"limit": 100}'

code Node
const response = await fetch("https://api.light-cloud.com/api/applications", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.LIGHT_CLOUD_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ limit: 100 }),
});

if (!response.ok) throw new Error(await response.text());

const { items } = await response.json();
items.forEach((app) => console.log(app.name, app.status));

code Python
import os
import requests

response = requests.post(
    "https://api.light-cloud.com/api/applications",
    headers={"Authorization": f"Bearer {os.environ['LIGHT_CLOUD_API_KEY']}"},
    json={"limit": 100},
)
response.raise_for_status()

for app in response.json()["items"]:
    print(app["name"], app["status"])

code Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.light-cloud.com/api/applications"))
    .header("Authorization", "Bearer " + System.getenv("LIGHT_CLOUD_API_KEY"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString("{\"limit\":100}"))
    .build();

var response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());

if (response.statusCode() >= 400) {
    throw new IllegalStateException(response.body());
}
System.out.println(response.body());

code Go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

func main() {
	body := bytes.NewBufferString(`{"limit":100}`)

	req, _ := http.NewRequest("POST",
		"https://api.light-cloud.com/api/applications", body)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("LIGHT_CLOUD_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var page struct {
		Items []struct {
			Name   string `json:"name"`
			Status string `json:"status"`
		} `json:"items"`
	}
	json.NewDecoder(res.Body).Decode(&page)

	for _, app := range page.Items {
		fmt.Println(app.Name, app.Status)
	}
}
```

## Retrieve an application

```endpoint
POST /api/applications/get
> One application with its environments. `status` is one of pending, building, deploying, healthy, degraded, failed, deleting.
permission: read:projects

param applicationId | string | required | The application to read
param targetOrganisationId | string | session only | Required when using a session token

response 200 | An application
{
  "id": "app_4kQ2",
  "name": "shop",
  "status": "healthy",
  "url": "https://main-shop-acme.light-cloud.io",
  "environments": [
    { "id": "env_7Tz", "name": "production", "is_production": true }
  ]
}

code curl
curl -X POST https://api.light-cloud.com/api/applications/get \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"applicationId": "app_4kQ2"}'

code Node
const API = "https://api.light-cloud.com";

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
const api = async (path, body = {}) => {
  const res = await fetch(API + path, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LIGHT_CLOUD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
};

const app = await api("/api/applications/get", {
  applicationId: "app_4kQ2",
});

console.log(app.url);

code Python
import os
import requests

API = "https://api.light-cloud.com"

# Everything this does is visible: POST, bearer token, JSON in, JSON out.
def api(path, **body):
    res = requests.post(
        API + path,
        headers={"Authorization": f"Bearer {os.environ['LIGHT_CLOUD_API_KEY']}"},
        json=body,
    )
    res.raise_for_status()
    return res.json()

app = api("/api/applications/get", applicationId="app_4kQ2")

print(app["url"])

code Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
static String apiPost(String path, String json) throws Exception {
    var request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.light-cloud.com" + path))
        .header("Authorization", "Bearer " + System.getenv("LIGHT_CLOUD_API_KEY"))
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(json))
        .build();

    var response = HttpClient.newHttpClient()
        .send(request, HttpResponse.BodyHandlers.ofString());

    if (response.statusCode() >= 400) throw new IllegalStateException(response.body());
    return response.body();
}

String response = apiPost(
    "/api/applications/get",
    "{\"applicationId\":\"app_4kQ2\"}");
```

## Retrieve status

```endpoint
POST /api/applications/status
> The same shape as retrieve, cheaper. Use it when polling a deploy rather than re-reading the whole record.
permission: read:projects

param applicationId | string | required | The application to poll
param targetOrganisationId | string | session only | Required when using a session token

response 200 | Current status
{
  "id": "app_4kQ2",
  "status": "building",
  "url": "https://main-shop-acme.light-cloud.io"
}

code curl
until [ "$(curl -sS -X POST https://api.light-cloud.com/api/applications/status \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"applicationId\":\"$APP_ID\"}" | jq -r .status)" = "healthy" ]; do
  sleep 10
done

code Node
const API = "https://api.light-cloud.com";

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
const api = async (path, body = {}) => {
  const res = await fetch(API + path, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LIGHT_CLOUD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
};

const settled = ["healthy", "failed", "degraded"];
let status = "pending";

while (!settled.includes(status)) {
  await new Promise((resolve) => setTimeout(resolve, 10_000));
  ({ status } = await api("/api/applications/status", {
    applicationId: appId,
  }));
}

if (status !== "healthy") throw new Error(`Deploy ended ${status}`);

code Python
import os
import requests

API = "https://api.light-cloud.com"

# Everything this does is visible: POST, bearer token, JSON in, JSON out.
def api(path, **body):
    res = requests.post(
        API + path,
        headers={"Authorization": f"Bearer {os.environ['LIGHT_CLOUD_API_KEY']}"},
        json=body,
    )
    res.raise_for_status()
    return res.json()

import time

SETTLED = {"healthy", "failed", "degraded"}
status = "pending"

while status not in SETTLED:
    time.sleep(10)
    status = api(
        "/api/applications/status", applicationId=app_id
    )["status"]

if status != "healthy":
    raise RuntimeError(f"Deploy ended {status}")

code Java
var settled = java.util.Set.of("healthy", "failed", "degraded");
String status = "pending";

while (!settled.contains(status)) {
    Thread.sleep(10_000);
    status = readStatus(appId);
}
```

## Detect a framework

```endpoint
POST /api/applications/detect-framework
> Reads a repository the way the console's create page does and returns the settings it would fill in. Call this before create rather than guessing.
permission: read:projects

param owner | string | required | Repository owner
param repo | string | required | Repository name
param branch | string | required | Branch to inspect
param rootDirectory | string | optional | Subdirectory, for a monorepo
param targetOrganisationId | string | session only | Required when using a session token

response 200 | Detected build settings
{
  "framework": "nextjs",
  "runtime": "nodejs",
  "deploymentType": "container",
  "buildCommand": "npm run build",
  "outputDirectory": ".next"
}

code curl
curl -X POST https://api.light-cloud.com/api/applications/detect-framework \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"owner": "acme", "repo": "shop", "branch": "main"}'

code Node
const API = "https://api.light-cloud.com";

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
const api = async (path, body = {}) => {
  const res = await fetch(API + path, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LIGHT_CLOUD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
};

const detected = await api("/api/applications/detect-framework", {
  owner: "acme",
  repo: "shop",
  branch: "main",
});

code Python
import os
import requests

API = "https://api.light-cloud.com"

# Everything this does is visible: POST, bearer token, JSON in, JSON out.
def api(path, **body):
    res = requests.post(
        API + path,
        headers={"Authorization": f"Bearer {os.environ['LIGHT_CLOUD_API_KEY']}"},
        json=body,
    )
    res.raise_for_status()
    return res.json()

detected = api(
    "/api/applications/detect-framework",
    owner="acme",
    repo="shop",
    branch="main",
)

code Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
static String apiPost(String path, String json) throws Exception {
    var request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.light-cloud.com" + path))
        .header("Authorization", "Bearer " + System.getenv("LIGHT_CLOUD_API_KEY"))
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(json))
        .build();

    var response = HttpClient.newHttpClient()
        .send(request, HttpResponse.BodyHandlers.ofString());

    if (response.statusCode() >= 400) throw new IllegalStateException(response.body());
    return response.body();
}

String detected = apiPost(
    "/api/applications/detect-framework",
    "{\"owner\":\"acme\",\"repo\":\"shop\",\"branch\":\"main\"}");
```

## Create an application

```endpoint
POST /api/applications/create
> Creates the application and its production environment, and starts the first build. Creates are not idempotent — if one times out, list before retrying.
permission: create:projects

param name | string | required | Becomes part of the generated subdomain
param githubRepoUrl | string | required | Repository URL on GitHub, GitLab or Bitbucket - must be reachable
param gitProvider | string | github | github, gitlab or bitbucket; a gitlab.com or bitbucket.org URL implies its provider
param gitlabProjectId | number | resolved | GitLab only - the numeric project id; resolved from the URL when omitted
param bitbucketRepoUuid | string | resolved | Bitbucket only - the repository uuid (braced); resolved from the URL when omitted
param githubBranch | string | main | The production environment's branch
param isPrivate | boolean | false | Private repositories need an installation (GitHub) or a connected GitLab or Bitbucket user
param projectId | string | optional | Folder to file it under
param deploymentType | string | static | static or container
param framework | string | react | See Frameworks
param runtime | string | optional | Container only
param buildCommand | string | detected | Command that produces the build
param outputDirectory | string | detected | Static only
param rootDirectory | string | optional | Monorepo subdirectory
param environmentVars | object | optional | String values only
param containerPort | number | detected | Container only
param memory | string | 512Mi | See Limits
param cpu | string | optional | Container only
param minInstances | number | 0 | 0 to 5
param maxInstances | number | 10 | 1 to 100, plan-capped
param concurrency | number | 80 | Requests served per instance
param cpuTarget | number | 80 | Percentage that triggers a scale-up
param region | string | optional | See Limits
param customDomain | string | optional | Can be added later
param autoDeployOnPush | boolean | optional | Deploy on every push
param autoDeployBranches | string[] | optional | Branches that auto-deploy
param autoDeleteStaleEnvs | boolean | optional | Remove environments for deleted branches

response 201 | The created application
{
  "id": "app_4kQ2",
  "name": "shop",
  "status": "pending",
  "url": "https://main-shop-acme.light-cloud.io"
}

code curl
curl -X POST https://api.light-cloud.com/api/applications/create \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "shop",
    "githubRepoUrl": "https://github.com/acme/shop",
    "githubBranch": "main",
    "deploymentType": "container",
    "framework": "nextjs",
    "runtime": "nodejs",
    "buildCommand": "npm run build",
    "environmentVars": { "NODE_ENV": "production" },
    "memory": "512Mi",
    "maxInstances": 5
  }'

code Node
const API = "https://api.light-cloud.com";

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
const api = async (path, body = {}) => {
  const res = await fetch(API + path, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LIGHT_CLOUD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
};

const app = await api("/api/applications/create", {
  name: "shop",
  githubRepoUrl: "https://github.com/acme/shop",
  githubBranch: "main",
  deploymentType: "container",
  framework: "nextjs",
  runtime: "nodejs",
  buildCommand: "npm run build",
  environmentVars: { NODE_ENV: "production" },
  memory: "512Mi",
  maxInstances: 5,
});

console.log(app.id);

code Python
import os
import requests

API = "https://api.light-cloud.com"

# Everything this does is visible: POST, bearer token, JSON in, JSON out.
def api(path, **body):
    res = requests.post(
        API + path,
        headers={"Authorization": f"Bearer {os.environ['LIGHT_CLOUD_API_KEY']}"},
        json=body,
    )
    res.raise_for_status()
    return res.json()

app = api(
    "/api/applications/create",
    name="shop",
    githubRepoUrl="https://github.com/acme/shop",
    githubBranch="main",
    deploymentType="container",
    framework="nextjs",
    runtime="nodejs",
    buildCommand="npm run build",
    environmentVars={"NODE_ENV": "production"},
    memory="512Mi",
    maxInstances=5,
)

print(app["id"])

code Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
static String apiPost(String path, String json) throws Exception {
    var request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.light-cloud.com" + path))
        .header("Authorization", "Bearer " + System.getenv("LIGHT_CLOUD_API_KEY"))
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(json))
        .build();

    var response = HttpClient.newHttpClient()
        .send(request, HttpResponse.BodyHandlers.ofString());

    if (response.statusCode() >= 400) throw new IllegalStateException(response.body());
    return response.body();
}

var body = """
    {
      "name": "shop",
      "githubRepoUrl": "https://github.com/acme/shop",
      "githubBranch": "main",
      "deploymentType": "container",
      "framework": "nextjs",
      "runtime": "nodejs",
      "buildCommand": "npm run build",
      "environmentVars": { "NODE_ENV": "production" },
      "memory": "512Mi",
      "maxInstances": 5
    }
    """;

String created = apiPost("/api/applications/create", body);
```

## Create from an upload

```endpoint
POST /api/applications/create-from-upload
> Builds an application from an archive instead of a repository. Nothing auto-deploys afterwards, since there is no repository to watch.
permission: create:projects

param name | string | required | Becomes part of the generated subdomain
param uploadId | string | required | From the uploads flow
param deploymentType | string | required | static or container
param projectId | string | optional | Folder to file it under
param framework | string | optional | Overrides detection
param runtime | string | optional | Container only
param buildCommand | string | optional | Command that produces the build
param outputDirectory | string | optional | Static only
param startCommand | string | optional | Container only
param environmentVars | object | optional | String values only

response 201 | The created application
{
  "id": "app_9Lm3",
  "name": "marketing-site",
  "source_type": "upload",
  "status": "pending"
}

code curl
curl -X POST https://api.light-cloud.com/api/applications/create-from-upload \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "marketing-site",
    "uploadId": "upl_8Xa1",
    "deploymentType": "static",
    "outputDirectory": "."
  }'

code Node
const API = "https://api.light-cloud.com";

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
const api = async (path, body = {}) => {
  const res = await fetch(API + path, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LIGHT_CLOUD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
};

const app = await api("/api/applications/create-from-upload", {
  name: "marketing-site",
  uploadId,
  deploymentType: "static",
  outputDirectory: ".",
});

code Python
import os
import requests

API = "https://api.light-cloud.com"

# Everything this does is visible: POST, bearer token, JSON in, JSON out.
def api(path, **body):
    res = requests.post(
        API + path,
        headers={"Authorization": f"Bearer {os.environ['LIGHT_CLOUD_API_KEY']}"},
        json=body,
    )
    res.raise_for_status()
    return res.json()

app = api(
    "/api/applications/create-from-upload",
    name="marketing-site",
    uploadId=upload_id,
    deploymentType="static",
    outputDirectory=".",
)
```

## Deploy

```endpoint
POST /api/applications/deploy
> Builds the current head of the environment's branch and releases it. Returns as soon as the deployment starts — poll status for the outcome.
permission: update:projects

param applicationId | string | required | The application to deploy
param environmentId | string | optional | Defaults to production
param targetOrganisationId | string | session only | Required when using a session token

response 200 | The deployment that just started
{
  "id": "dep_9f2a",
  "environment_id": "env_7Tz",
  "status": "pending",
  "commit_sha": "9f3c1ab",
  "started_at": "2026-08-28T07:00:12.000Z"
}

code curl
curl -X POST https://api.light-cloud.com/api/applications/deploy \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"applicationId": "app_4kQ2"}'

code Node
const API = "https://api.light-cloud.com";

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
const api = async (path, body = {}) => {
  const res = await fetch(API + path, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LIGHT_CLOUD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
};

const deployment = await api("/api/applications/deploy", {
  applicationId: "app_4kQ2",
});

console.log("started", deployment.id);

code Python
import os
import requests

API = "https://api.light-cloud.com"

# Everything this does is visible: POST, bearer token, JSON in, JSON out.
def api(path, **body):
    res = requests.post(
        API + path,
        headers={"Authorization": f"Bearer {os.environ['LIGHT_CLOUD_API_KEY']}"},
        json=body,
    )
    res.raise_for_status()
    return res.json()

deployment = api(
    "/api/applications/deploy", applicationId="app_4kQ2"
)

print("started", deployment["id"])

code Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
static String apiPost(String path, String json) throws Exception {
    var request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.light-cloud.com" + path))
        .header("Authorization", "Bearer " + System.getenv("LIGHT_CLOUD_API_KEY"))
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(json))
        .build();

    var response = HttpClient.newHttpClient()
        .send(request, HttpResponse.BodyHandlers.ofString());

    if (response.statusCode() >= 400) throw new IllegalStateException(response.body());
    return response.body();
}

String deployment = apiPost(
    "/api/applications/deploy",
    "{\"applicationId\":\"app_4kQ2\"}");
```

## Rename

```endpoint
POST /api/applications/rename
> Renaming changes the generated subdomain, and the old URL stops resolving.
permission: update:projects

param applicationId | string | required | The application to rename
param name | string | required | The new name

response 200 | The updated application
{ "id": "app_4kQ2", "name": "storefront", "url": "https://main-storefront-acme.light-cloud.io" }

code curl
curl -X POST https://api.light-cloud.com/api/applications/rename \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"applicationId": "app_4kQ2", "name": "storefront"}'

code Node
const API = "https://api.light-cloud.com";

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
const api = async (path, body = {}) => {
  const res = await fetch(API + path, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LIGHT_CLOUD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
};

await api("/api/applications/rename", {
  applicationId: "app_4kQ2",
  name: "storefront",
});

code Python
import os
import requests

API = "https://api.light-cloud.com"

# Everything this does is visible: POST, bearer token, JSON in, JSON out.
def api(path, **body):
    res = requests.post(
        API + path,
        headers={"Authorization": f"Bearer {os.environ['LIGHT_CLOUD_API_KEY']}"},
        json=body,
    )
    res.raise_for_status()
    return res.json()

api(
    "/api/applications/rename",
    applicationId="app_4kQ2",
    name="storefront",
)
```

## Move

```endpoint
POST /api/applications/move
> Files the application under a different folder.
permission: update:projects

param applicationId | string | required | The application to move
param targetFolderId | string | required | Folder id, or null for the root

response 200 | The updated application
{ "id": "app_4kQ2", "project_id": "prj_2Bd" }

code curl
curl -X POST https://api.light-cloud.com/api/applications/move \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"applicationId": "app_4kQ2", "targetFolderId": "prj_2Bd"}'

code Node
const API = "https://api.light-cloud.com";

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
const api = async (path, body = {}) => {
  const res = await fetch(API + path, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LIGHT_CLOUD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
};

await api("/api/applications/move", {
  applicationId: "app_4kQ2",
  targetFolderId: null, // root
});
```

## Delete

```endpoint
DELETE /api/applications/delete
> Tears down every environment. Not reversible, and issued as a POST despite the verb shown here.
permission: delete:projects

param applicationId | string | required | The application to delete
param targetOrganisationId | string | session only | Required when using a session token

response 200 | Deletion accepted
{ "message": "Application deleted" }

code curl
curl -X POST https://api.light-cloud.com/api/applications/delete \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"applicationId": "app_4kQ2"}'

code Node
const API = "https://api.light-cloud.com";

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
const api = async (path, body = {}) => {
  const res = await fetch(API + path, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LIGHT_CLOUD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
};

await api("/api/applications/delete", {
  applicationId: "app_4kQ2",
});

code Python
import os
import requests

API = "https://api.light-cloud.com"

# Everything this does is visible: POST, bearer token, JSON in, JSON out.
def api(path, **body):
    res = requests.post(
        API + path,
        headers={"Authorization": f"Bearer {os.environ['LIGHT_CLOUD_API_KEY']}"},
        json=body,
    )
    res.raise_for_status()
    return res.json()

api("/api/applications/delete", applicationId="app_4kQ2")
```

## Custom domains

Each of these takes either `applicationId` (which targets the production environment) or `environmentId`. See [Custom domains](/platform/custom-domains) for the DNS records to create.

```endpoint
POST /api/applications/add-domain
> Attaches a domain and starts certificate issuance. Certificates are issued asynchronously — poll check-domain for progress.
permission: update:projects

param applicationId | string | one of two | Targets the production environment
param environmentId | string | one of two | Targets a specific environment
param domain | string | required | The hostname to attach

response 200 | Domain attached, verification pending
{
  "domain": "shop.example.com",
  "dns_verified": false,
  "certificate_status": "provisioning"
}

code curl
curl -X POST https://api.light-cloud.com/api/applications/add-domain \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"applicationId": "app_4kQ2", "domain": "shop.example.com"}'

code Node
const API = "https://api.light-cloud.com";

// Everything this does is visible: POST, bearer token, JSON in, JSON out.
const api = async (path, body = {}) => {
  const res = await fetch(API + path, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LIGHT_CLOUD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
};

await api("/api/applications/add-domain", {
  applicationId: "app_4kQ2",
  domain: "shop.example.com",
});

const state = await api("/api/applications/check-domain", {
  applicationId: "app_4kQ2",
});

code Python
import os
import requests

API = "https://api.light-cloud.com"

# Everything this does is visible: POST, bearer token, JSON in, JSON out.
def api(path, **body):
    res = requests.post(
        API + path,
        headers={"Authorization": f"Bearer {os.environ['LIGHT_CLOUD_API_KEY']}"},
        json=body,
    )
    res.raise_for_status()
    return res.json()

api(
    "/api/applications/add-domain",
    applicationId="app_4kQ2",
    domain="shop.example.com",
)

state = api(
    "/api/applications/check-domain", applicationId="app_4kQ2"
)
```

| Endpoint | Extra parameters | Description |
| --- | --- | --- |
| `POST /api/applications/check-domain` | | Reports DNS and certificate state |
| `POST /api/applications/update-domain` | `newDomain` | Replaces the attached domain |
| `POST /api/applications/retry-domain` | | Retries a failed verification |
| `POST /api/applications/remove-domain` | `revertToAutoSubdomain` (default `true`) | Detaches it |

## Repository helpers

| Endpoint | Description |
| --- | --- |
| `POST /api/applications/list-repo-directories` | Folders in a repository, for root-directory pickers |
| `POST /api/applications/public-branches` | Branches of a public repository, no installation needed |
| `GET /api/applications/lookup/:subdomain` | Which application owns a subdomain |

## Related

- [Environments](/reference/api/environments): Branches, variables, scaling, and logs.
- [Deployments](/reference/api/deployments): History and rollback.
- [GitHub](/reference/api/github): Making a private repository reachable.
