Koo Docs
View as Markdown

Apps

List the account's published apps (with live-derived URL + blockers), newest first

GET/accounts/{accountId}/apps
Rolesreadereditoradminowner
Parameters
cursorstringquery

Opaque cursor (page.nextCursor).

limitnumberquery

Page size (1–100, default 20).

accountIdstringpathrequired
Returns
200 · PublishedAppsPageDto
dataobject[]read-only
pageobjectread-only
cURL
GET /accounts/{accountId}/apps
bash
curl https://api.koo.io/accounts/:accountId/apps \  -H "Authorization: Bearer kc_your_api_token"
Response
json
{  "data": [    {      "id": "acct_01example0000000000000000x",      "accountId": "acct_01example0000000000000000x",      "projectId": "project_01example0000000000000000x",      "environmentName": "example",      "serviceName": "example",      "urlChoice": "example",      "slug": "my-app",      "profile": {        "displayName": "My app",        "tagline": "example",        "description": "example",        "images": [          "https://example.com"        ],        "youtubeId": "youtube_01example0000000000000000x",        "videoCover": true,        "category": "example",        "tags": [          "example"        ],        "links": {          "website": "https://example.com",          "repo": "https://example.com",          "appStore": "https://example.com",          "playStore": "https://example.com",          "steam": "https://example.com"        }      },      "url": "https://example.com",      "blockers": [        "service_missing"      ],      "createdAt": "2026-01-01T00:00:00.000Z",      "updatedAt": "2026-01-01T00:00:00.000Z"    }  ],  "page": {    "nextCursor": "example",    "hasMore": true  }}

Publish a service as a showcase app

POST/accounts/{accountId}/apps
Rolesreadereditoradminowner

Validates the pointer live (project in account; environment + exposed web service by name; URL is the generated koo URL or an active custom domain of that service) and the publish gate (tagline + category).

Parameters
accountIdstringpathrequired
Request body
projectIdstringrequired

The project the service to publish lives in (must belong to the account).

environmentNamestringrequired

Name of the environment the service lives in.

serviceNamestringrequired

Name of the exposed web service to publish (by name).

urlChoicestring

Either 'generated' (the koo URL from the service's status) or one of the service's active custom domain hostnames.

slugstringrequired

The public URL slug under the owner handle (url-safe word, 3-16 chars, account-unique).

profileobjectrequired

The initial listing content (tagline + category are required to publish).

Returns
200 · PublishedAppDto409 · ErrorEnvelopeDto
idstringread-only

Identifier of the published app.

accountIdstringread-only

Identifier of the owning account.

projectIdstringrequired

Identifier of the project the pointed-at service lives in.

environmentNamestringrequired

Name of the environment the pointed-at service lives in (by name, not id).

serviceNamestringrequired

Name of the exposed web service this listing points at (by name, not id).

urlChoicestringrequired

Either 'generated' (the koo URL from the service's status) or one of the service's active custom domain hostnames.

slugstringrequired

URL slug of the app under the owner's handle (/@handle/:slug).

profileobjectrequired
urlstring <uri>read-only

The live-derived public URL to visit, when resolvable (absent while degraded with no fallback).

blockers"service_missing" | "not_exposed" | "domain_missing"[]read-only

Live-derived degradations on the pointer — the honest reasons shown to the owner. Empty when healthy.

createdAtstring <date-time>read-only

When the app was published.

updatedAtstring <date-time>read-only

When the app or its listing was last updated.

cURL
POST /accounts/{accountId}/apps
bash
curl https://api.koo.io/accounts/:accountId/apps \  -X POST \  -H "Authorization: Bearer kc_your_api_token" \  -H "Content-Type: application/json" \  -d '{      "projectId": "project_01example0000000000000000x",      "environmentName": "example",      "serviceName": "example",      "slug": "my-app",      "profile": {}    }'
Response
json
{  "id": "acct_01example0000000000000000x",  "accountId": "acct_01example0000000000000000x",  "projectId": "project_01example0000000000000000x",  "environmentName": "example",  "serviceName": "example",  "urlChoice": "example",  "slug": "my-app",  "profile": {    "displayName": "My app",    "tagline": "example",    "description": "example",    "images": [      "https://example.com"    ],    "youtubeId": "youtube_01example0000000000000000x",    "videoCover": true,    "category": "example",    "tags": [      "example"    ],    "links": {      "website": "https://example.com",      "repo": "https://example.com",      "appStore": "https://example.com",      "playStore": "https://example.com",      "steam": "https://example.com"    }  },  "url": "https://example.com",  "blockers": [    "service_missing"  ],  "createdAt": "2026-01-01T00:00:00.000Z",  "updatedAt": "2026-01-01T00:00:00.000Z"}

Get a published app (owner view)

GET/accounts/{accountId}/apps/{appId}
Rolesreadereditoradminowner
Parameters
appIdstringpathrequired
accountIdstringpathrequired
Returns
200 · PublishedAppDto404 · ErrorEnvelopeDto
idstringread-only

Identifier of the published app.

accountIdstringread-only

Identifier of the owning account.

projectIdstringrequired

Identifier of the project the pointed-at service lives in.

environmentNamestringrequired

Name of the environment the pointed-at service lives in (by name, not id).

serviceNamestringrequired

Name of the exposed web service this listing points at (by name, not id).

urlChoicestringrequired

Either 'generated' (the koo URL from the service's status) or one of the service's active custom domain hostnames.

slugstringrequired

URL slug of the app under the owner's handle (/@handle/:slug).

profileobjectrequired
urlstring <uri>read-only

The live-derived public URL to visit, when resolvable (absent while degraded with no fallback).

blockers"service_missing" | "not_exposed" | "domain_missing"[]read-only

Live-derived degradations on the pointer — the honest reasons shown to the owner. Empty when healthy.

createdAtstring <date-time>read-only

When the app was published.

updatedAtstring <date-time>read-only

When the app or its listing was last updated.

cURL
GET /accounts/{accountId}/apps/{appId}
bash
curl https://api.koo.io/accounts/:accountId/apps/:appId \  -H "Authorization: Bearer kc_your_api_token"
Response
json
{  "id": "acct_01example0000000000000000x",  "accountId": "acct_01example0000000000000000x",  "projectId": "project_01example0000000000000000x",  "environmentName": "example",  "serviceName": "example",  "urlChoice": "example",  "slug": "my-app",  "profile": {    "displayName": "My app",    "tagline": "example",    "description": "example",    "images": [      "https://example.com"    ],    "youtubeId": "youtube_01example0000000000000000x",    "videoCover": true,    "category": "example",    "tags": [      "example"    ],    "links": {      "website": "https://example.com",      "repo": "https://example.com",      "appStore": "https://example.com",      "playStore": "https://example.com",      "steam": "https://example.com"    }  },  "url": "https://example.com",  "blockers": [    "service_missing"  ],  "createdAt": "2026-01-01T00:00:00.000Z",  "updatedAt": "2026-01-01T00:00:00.000Z"}

Re-point a published app at a different service and/or URL

PATCH/accounts/{accountId}/apps/{appId}
Rolesreadereditoradminowner

Re-validates the resulting pointer live (same rules as publish). A structural change to the public listing, so it requires the publish permission.

Parameters
appIdstringpathrequired
accountIdstringpathrequired
Request body
environmentNamestring

New environment name for the pointer.

serviceNamestring

New exposed web service name for the pointer.

urlChoicestring

New URL choice ('generated' or an active custom domain hostname).

Returns
200 · PublishedAppDto
idstringread-only

Identifier of the published app.

accountIdstringread-only

Identifier of the owning account.

projectIdstringrequired

Identifier of the project the pointed-at service lives in.

environmentNamestringrequired

Name of the environment the pointed-at service lives in (by name, not id).

serviceNamestringrequired

Name of the exposed web service this listing points at (by name, not id).

urlChoicestringrequired

Either 'generated' (the koo URL from the service's status) or one of the service's active custom domain hostnames.

slugstringrequired

URL slug of the app under the owner's handle (/@handle/:slug).

profileobjectrequired
urlstring <uri>read-only

The live-derived public URL to visit, when resolvable (absent while degraded with no fallback).

blockers"service_missing" | "not_exposed" | "domain_missing"[]read-only

Live-derived degradations on the pointer — the honest reasons shown to the owner. Empty when healthy.

createdAtstring <date-time>read-only

When the app was published.

updatedAtstring <date-time>read-only

When the app or its listing was last updated.

cURL
PATCH /accounts/{accountId}/apps/{appId}
bash
curl https://api.koo.io/accounts/:accountId/apps/:appId \  -X PATCH \  -H "Authorization: Bearer kc_your_api_token" \  -H "Content-Type: application/json" \  -d '{}'
Response
json
{  "id": "acct_01example0000000000000000x",  "accountId": "acct_01example0000000000000000x",  "projectId": "project_01example0000000000000000x",  "environmentName": "example",  "serviceName": "example",  "urlChoice": "example",  "slug": "my-app",  "profile": {    "displayName": "My app",    "tagline": "example",    "description": "example",    "images": [      "https://example.com"    ],    "youtubeId": "youtube_01example0000000000000000x",    "videoCover": true,    "category": "example",    "tags": [      "example"    ],    "links": {      "website": "https://example.com",      "repo": "https://example.com",      "appStore": "https://example.com",      "playStore": "https://example.com",      "steam": "https://example.com"    }  },  "url": "https://example.com",  "blockers": [    "service_missing"  ],  "createdAt": "2026-01-01T00:00:00.000Z",  "updatedAt": "2026-01-01T00:00:00.000Z"}

Unpublish an app (soft-delete; republish re-creates)

DELETE/accounts/{accountId}/apps/{appId}
Rolesreadereditoradminowner
Parameters
appIdstringpathrequired
accountIdstringpathrequired
Returns
204
cURL
DELETE /accounts/{accountId}/apps/{appId}
bash
curl https://api.koo.io/accounts/:accountId/apps/:appId \  -X DELETE \  -H "Authorization: Bearer kc_your_api_token"

Edit a published app listing (content, tags, links, image order)

PATCH/accounts/{accountId}/apps/{appId}/profile
Rolesreadereditoradminowner

Owner/editor edit of the showcase fields (displayName/tagline/description/youtubeId/category/tags/links + image reorder/remove). Adding an image goes through the upload commit (scope appImage). Repopulates discovery search.

Parameters
appIdstringpathrequired
accountIdstringpathrequired
Request body
displayNamestring | null

Public pretty name (null to clear — the app slug is then shown).

taglinestring | null

Short one-line summary (null to clear).

descriptionstring | null

Long-form description for the showcase page (null to clear).

youtubeIdstring | null

YouTube video id embedded as a demo/trailer (null to clear).

videoCoverboolean

When a video is set, use the video's YouTube thumbnail as the cover (true) or fall back to the first gallery image (false).

category"web" | "api" | "devtools" | "ai-ml" | "data" | "bots" | "games" | "ecommerce" | "social" | "productivity" | "education" | "other" | null

Discovery category slug; must be one of the configured categories (null to clear).

tagsstring[]

Freeform discovery tags (empty array to clear).

linksobject

External links shown on the showcase page (replaces the whole set).

imagesstring <uri>[]

The reordered/pruned gallery (replaces the order; images[0] is the cover). Only URLs already committed to this app may appear — adding a new image goes through the upload commit.

Returns
200 · PublishedAppDto
idstringread-only

Identifier of the published app.

accountIdstringread-only

Identifier of the owning account.

projectIdstringrequired

Identifier of the project the pointed-at service lives in.

environmentNamestringrequired

Name of the environment the pointed-at service lives in (by name, not id).

serviceNamestringrequired

Name of the exposed web service this listing points at (by name, not id).

urlChoicestringrequired

Either 'generated' (the koo URL from the service's status) or one of the service's active custom domain hostnames.

slugstringrequired

URL slug of the app under the owner's handle (/@handle/:slug).

profileobjectrequired
urlstring <uri>read-only

The live-derived public URL to visit, when resolvable (absent while degraded with no fallback).

blockers"service_missing" | "not_exposed" | "domain_missing"[]read-only

Live-derived degradations on the pointer — the honest reasons shown to the owner. Empty when healthy.

createdAtstring <date-time>read-only

When the app was published.

updatedAtstring <date-time>read-only

When the app or its listing was last updated.

cURL
PATCH /accounts/{accountId}/apps/{appId}/profile
bash
curl https://api.koo.io/accounts/:accountId/apps/:appId/profile \  -X PATCH \  -H "Authorization: Bearer kc_your_api_token" \  -H "Content-Type: application/json" \  -d '{}'
Response
json
{  "id": "acct_01example0000000000000000x",  "accountId": "acct_01example0000000000000000x",  "projectId": "project_01example0000000000000000x",  "environmentName": "example",  "serviceName": "example",  "urlChoice": "example",  "slug": "my-app",  "profile": {    "displayName": "My app",    "tagline": "example",    "description": "example",    "images": [      "https://example.com"    ],    "youtubeId": "youtube_01example0000000000000000x",    "videoCover": true,    "category": "example",    "tags": [      "example"    ],    "links": {      "website": "https://example.com",      "repo": "https://example.com",      "appStore": "https://example.com",      "playStore": "https://example.com",      "steam": "https://example.com"    }  },  "url": "https://example.com",  "blockers": [    "service_missing"  ],  "createdAt": "2026-01-01T00:00:00.000Z",  "updatedAt": "2026-01-01T00:00:00.000Z"}

An account's published apps as discovery cards (fully-broken listings hidden)

GET/profiles/{handle}/apps
Parameters
handlestringpathrequired
Returns
200 · PublishedAppCardsPageDto404 · ErrorEnvelopeDto
itemsobject[]read-only
nextCursorstring | nullread-only
cURL
GET /profiles/{handle}/apps
bash
curl https://api.koo.io/profiles/:handle/apps \  -H "Authorization: Bearer kc_your_api_token"
Response
json
{  "items": [    {      "id": "acct_01example0000000000000000x",      "accountId": "acct_01example0000000000000000x",      "handle": "alice",      "slug": "my-app",      "name": "my-app",      "tagline": "example",      "photoUrl": "https://example.com",      "youtubeId": "youtube_01example0000000000000000x",      "videoCover": true,      "category": "example",      "tags": [        "example"      ],      "region": "example",      "score": 1,      "createdAt": "2026-01-01T00:00:00.000Z"    }  ],  "nextCursor": "example"}

Public app detail — profile, images, derived Visit URL, comments (depth ≤ 1), vote/follow state (when authed)

GET/profiles/{handle}/apps/{slug}
Parameters
slugstringpathrequired
handlestringpathrequired
Returns
200 · PublicAppDto404 · ErrorEnvelopeDto
idstringread-only

Identifier of the published app.

accountIdstringread-only

Identifier of the owning account.

slugstringread-only

URL slug of the app under the owner's handle.

namestringread-only

Display name of the app (profile displayName, falling back to the slug).

authorobjectread-only
profileobjectread-only
urlstring <uri> | nullread-only

The public URL to visit, or null when the listing is degraded (the Visit CTA is then hidden).

regionstring | nullread-only

Primary deploy region — the identifier of the app's exposed service's first enabled location (e.g. `aws-us-east-2`); null when the service or its region can no longer be resolved.

countsobjectread-only

Aggregate social counts for the app.

commentsobjectread-only

The first page of the app's threaded comments (newest first; replies nest one level via parentId).

viewerVote-1 | 1read-only

The viewing account's current vote (+1 up, -1 down) — present only on an authenticated read with a vote cast.

authorFollowStateobjectread-only

The viewing account's follow relationship to the author — present only on an authenticated read.

createdAtstring <date-time>read-only

When the app was published.

cURL
GET /profiles/{handle}/apps/{slug}
bash
curl https://api.koo.io/profiles/:handle/apps/:slug \  -H "Authorization: Bearer kc_your_api_token"
Response
json
{  "id": "acct_01example0000000000000000x",  "accountId": "acct_01example0000000000000000x",  "slug": "my-app",  "name": "my-app",  "author": {    "id": "acct_01example0000000000000000x",    "handle": "alice",    "displayName": "My app",    "avatarUrl": "https://example.com"  },  "profile": {    "displayName": "My app",    "tagline": "example",    "description": "example",    "images": [      "https://example.com"    ],    "youtubeId": "youtube_01example0000000000000000x",    "videoCover": true,    "category": "example",    "tags": [      "example"    ],    "links": {      "website": "https://example.com",      "repo": "https://example.com",      "appStore": "https://example.com",      "playStore": "https://example.com",      "steam": "https://example.com"    }  },  "url": "https://example.com",  "region": "example",  "counts": {    "comments": 0,    "score": 1  },  "comments": {    "data": [      {        "id": "acct_01example0000000000000000x",        "author": {          "id": "acct_01example0000000000000000x",          "handle": "alice",          "displayName": "My app",          "avatarUrl": "https://example.com"        },        "body": "example",        "score": 1,        "viewerVote": -1,        "parentId": "parent_01example0000000000000000x",        "createdAt": "2026-01-01T00:00:00.000Z",        "deletedAt": "2026-01-01T00:00:00.000Z",        "replies": {          "data": [            {              "id": null,              "author": null,              "body": null,              "score": null,              "viewerVote": null,              "parentId": null,              "createdAt": null,              "deletedAt": null            }          ],          "page": {            "nextCursor": "example",            "hasMore": true          }        },        "replyCount": 0      }    ],    "page": {      "nextCursor": "example",      "hasMore": true    }  },  "viewerVote": -1,  "authorFollowState": {    "following": true,    "followedBy": true  },  "createdAt": "2026-01-01T00:00:00.000Z"}