API reference
Apps
List the apps installed in the workspace and what workers may do in each, and install an app by its address.
An app is a service a worker can use, such as a calendar, a mail account or a tool of your own. A person connects an app in the Little Worker app, signs in to it, and sets what workers may do in it: Read for the tools that only look, and Act for the tools that change things.
These routes list the apps in your workspace and install one by its address. Signing in to an app by hand, and changing its Read and Act levels, are a person's to do in the app. No route here does either, and no answer shows a sign-in. Apps explains Read and Act, how a worker gets an app, and apps on a private network.
The app object
Both routes on this page answer an app, or a list of them.
Fields
typestringAlwaysapp.idapp idThe app's id:app_and a whole number, such asapp_12.namestringThe app's name.urlstring or nullThe address of the app's server in its plain form: a lowercase scheme and host, no default port, no trailing slash, and less any query, which may hold a key the app was installed with.nullwhen the app has no web address.connectedbooleanWhether Little Worker holds a sign-in for the app: a person signed in to it, or an app sign-in or fixed token for its address is saved in the workspace's shared password set. The sign-in itself never shows.readstringWhether workers may use the app's tools that only read:onlets them,offdoes not.One of
on,offactstringWhat workers may do in the app that changes things:allowlets them act without asking,askasks a person first (see Approvals), andoffnever lets them act. Underallow, a step that sends, pays, posts or deletes still asks.One of
allow,ask,off
{
"type": "app",
"id": "app_12",
"name": "Acme CRM",
"url": "https://crm.example.com/mcp",
"connected": true,
"read": "on",
"act": "ask"
}List apps
GET/apps
List the apps installed in the key's workspace, newest first, whether or not they are connected. order and the created_at filters go by the time the app was added.
Query parameters
limitintegerHow many to answer, 1 to 100. The default is 20.pagestringThenext_pageof the answer before, to read the next page.orderstringdesc, newest first (the default), orasc, oldest first.One of
desc,asccreated_at[gt]timeOnly those made after this time, an RFC 3339 time compared to the millisecond the answer shows.created_at[gte],created_at[lt]andcreated_at[lte]work the same way.
Returns
A list: data holds the apps, and next_page is the page to send for the next ones (null on the last page).
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | A filter is not valid, such as a limit outside 1 to 100 (error_code validation_error). page is not a page this list gave with the same order and created_at filters (error_code invalid_cursor). Read the first page again. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/apps?limit=2" \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19"const response = await fetch("https://core.thelittleworker.com/api/v1/apps?limit=2", {
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
const apps = await response.json()import os
import requests
response = requests.get(
"https://core.thelittleworker.com/api/v1/apps",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
params={
"limit": "2",
},
)
apps = response.json(){
"data": [
{
"type": "app",
"id": "app_13",
"name": "Price list",
"url": "https://example.com/mcp",
"connected": false,
"read": "on",
"act": "ask"
},
{
"type": "app",
"id": "app_12",
"name": "Acme CRM",
"url": "https://crm.example.com/mcp",
"connected": true,
"read": "on",
"act": "ask"
}
],
"next_page": null
}Install an app
POST/apps
Install an app by the address of its server. The app starts with Read on and Act ask, and connected is false until the app is signed in.
The address must be an https address on the public internet. A user name or password in it is dropped, not kept. The host must resolve, and every address it resolves to must be public. Little Worker's servers refuse a loopback address, such as localhost. Little Worker running on your own computer takes one, over http too. An address under the domain of one of your tunnels is reached through that tunnel, and never over the public internet. An address that is already installed answers the app that is there, with its own name, so a retry is safe.
Body
namestringRequiredThe app's name, 1 to 80 characters. Two apps cannot share a name. The comparison ignores case and reads each run of characters other than the lettersatozand digits as one hyphen, soAcme CRMandacme-crmclash.urlstringRequiredThe address of the app's server, 1 to 2,048 characters.
Returns
The app.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | name or url is missing, empty or too long, the body has a field the route does not take, or the address is refused (error_code validation_error). For an address, message says why: An app's url must be an http or https address. (it is no address, or has another scheme), The connector address could not be resolved., The connector address is a private network address. (also an address under the domain of a tunnel that is not yours), The connector address is a loopback address. (on Little Worker's servers), or The connector address is not HTTPS. (an http address that is not loopback, or any http address under a tunnel domain). |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 409 | conflict_error | An app with the same name is already installed at another address (error_code conflict). Choose another name. |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/apps" \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19" \
-H "content-type: application/json" \
-d '{
"name": "Price list",
"url": "https://example.com/mcp"
}'const response = await fetch("https://core.thelittleworker.com/api/v1/apps", {
method: "POST",
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
"content-type": "application/json",
},
body: JSON.stringify({
name: "Price list",
url: "https://example.com/mcp",
}),
})
const app = await response.json()import os
import requests
response = requests.post(
"https://core.thelittleworker.com/api/v1/apps",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
json={
"name": "Price list",
"url": "https://example.com/mcp",
},
)
app = response.json(){
"type": "app",
"id": "app_13",
"name": "Price list",
"url": "https://example.com/mcp",
"connected": false,
"read": "on",
"act": "ask"
}