App Sites
Create, list, get, and delete app sites via the OrbitKit API.
App sites are the top-level resource in OrbitKit. Each app site has its own privacy policy, support page, custom domain, and subscription. A single account can have up to 10 app sites.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/apps |
List all apps |
| POST | /api/apps |
Create an app |
| GET | /api/apps/:appId |
Get a single app |
| DELETE | /api/apps/:appId |
Delete an app |
| GET | /api/apps/:appId/site |
Get site configuration |
| PATCH | /api/apps/:appId/site |
Update site configuration |
List all apps
GET /api/apps
Returns all apps for the authenticated user.
Response
[
{
"appId": "-NtestApp123",
"appName": "My Weather App",
"slug": "my-weather-app",
"createdAt": 1712345678000,
"updatedAt": 1712345678000
}
]
Returns an empty array [] if the user has no apps.
Create an app
POST /api/apps
Creates a new app with an auto-generated URL slug derived from the app name.
Rate limit: 5/hour per user
Request body
| Field | Type | Required | Description |
|---|---|---|---|
appName |
string | Yes | App display name (1–200 chars) |
Full example
curl -X POST https://api.orbitkit.io/api/apps \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"appName": "My Weather App"}'
var request = URLRequest(url: URL(string: "https://api.orbitkit.io/api/apps")!)
request.httpMethod = "POST"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode(["appName": "My Weather App"])
let (data, response) = try await URLSession.shared.data(for: request)
let result = try JSONDecoder().decode(CreateAppResponse.self, from: data)
print(result.appId) // "-NtestApp123"
const res = await fetch("https://api.orbitkit.io/api/apps", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ appName: "My Weather App" }),
});
const { appId, appName, slug } = await res.json();
Response 201 Created
{
"appId": "-NtestApp123",
"appName": "My Weather App",
"slug": "my-weather-app"
}
Errors
| Code | Status | When |
|---|---|---|
VALIDATION_FAILED |
400 | appName is missing or invalid |
APP_LIMIT_REACHED |
400 | Account already has 10 apps |
SLUG_TAKEN |
409 | Generated slug is already in use |
Get a single app
GET /api/apps/:appId
Returns app details including the site configuration.
Response
{
"appId": "-NtestApp123",
"appName": "My Weather App",
"site": {
"appName": "My Weather App",
"description": "Accurate weather forecasts",
"slug": "my-weather-app",
"iconUrl": "https://storage.googleapis.com/.../icon.png",
"deployed": true,
"deployedAt": 1712345678000,
"createdAt": 1712345678000,
"updatedAt": 1712345678000
}
}
The site field is null if the app has not been configured yet.
Delete an app
DELETE /api/apps/:appId
Permanently deletes the app and all associated data:
- Releases the URL slug
- Removes the custom domain and SSL certificate
- Cancels the app’s Stripe subscription
- Deletes all files from storage
- Removes the app record from the database
Response 204 No Content
No response body.
Get site configuration
GET /api/apps/:appId/site
Returns the site configuration for an app.
Response
{
"appName": "My Weather App",
"description": "Accurate weather forecasts",
"slug": "my-weather-app",
"iconUrl": "https://storage.googleapis.com/.../icon.png",
"customDomain": "privacy.myapp.com",
"subscriptionStatus": "active",
"deployed": true,
"deployedAt": 1712345678000,
"createdAt": 1712345678000,
"updatedAt": 1712345678000
}
Update site configuration
PATCH /api/apps/:appId/site
Partially updates site fields. Only provided fields are changed.
Request body
All fields are optional:
| Field | Type | Description |
|---|---|---|
appName |
string | Display name (1–200 chars) |
description |
string | Site description (max 4000 chars) |
slug |
string | URL slug (1–63 chars, lowercase alphanumeric + hyphens) |
Full example
curl -X PATCH https://api.orbitkit.io/api/apps/-NtestApp123/site \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"description": "Accurate weather forecasts for iOS"}'
var request = URLRequest(url: URL(string: "https://api.orbitkit.io/api/apps/-NtestApp123/site")!)
request.httpMethod = "PATCH"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode(
["description": "Accurate weather forecasts for iOS"]
)
let (data, _) = try await URLSession.shared.data(for: request)
let site = try JSONDecoder().decode(SiteConfig.self, from: data)
const res = await fetch(
"https://api.orbitkit.io/api/apps/-NtestApp123/site",
{
method: "PATCH",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
description: "Accurate weather forecasts for iOS",
}),
}
);
const site = await res.json();
Response
Returns the full merged site object (same shape as GET /api/apps/:appId/site).
Errors
| Code | Status | When |
|---|---|---|
VALIDATION_FAILED |
400 | Invalid field value |
SLUG_TAKEN |
409 | New slug is already in use |