Source
This app’s code and revision history are public. Download a copy or explore the files below.
Public
The app’s code and revision history are public.
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone ee59501e91bae6d2c23a8370
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
public/openapi.json
{
"openapi": "3.1.0",
"info": {
"title": "Lists API",
"version": "1.0.0",
"summary": "Free shared grocery and to-do lists that update live for everyone in the house.",
"description": "Make shared lists, add items, check them off and read them back. Making a list needs no key; everything else needs the list key in the x-list-key header (returned when a list is made, or copied from the list's Share screen in the app). The key works like the share link: anyone with it can read and change the list. Errors are JSON with an error sentence that says what to change. Free with no limits. Full guide: https://lists-free.openapps.xyz/llms.txt",
"contact": {
"name": "OpenApps",
"url": "https://openapps.free/"
}
},
"servers": [
{
"url": "https://functions.openapps.xyz/apps/ee59501e91bae6d2c23a8370/api"
}
],
"externalDocs": {
"description": "llms.txt",
"url": "https://lists-free.openapps.xyz/llms.txt"
},
"paths": {
"/": {
"get": {
"operationId": "describe",
"summary": "Describe the API and its routes",
"responses": {
"200": {
"description": "Name, docs link and routes",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"about": {
"type": "string"
},
"docs": {
"type": "string"
},
"openapi": {
"type": "string"
},
"routes": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"/lists": {
"post": {
"operationId": "createList",
"summary": "Make a new shared list (no key needed)",
"description": "Returns the list key for later calls, a share link for the household and an admin link for the owner.",
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateList"
},
"example": {
"name": "Groceries",
"items": [
"milk and eggs",
"2 lb chicken"
]
}
}
}
},
"responses": {
"201": {
"description": "The new list",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/ListSummary"
},
{
"type": "object",
"required": [
"key",
"shareUrl",
"adminUrl"
],
"properties": {
"key": {
"type": "string",
"description": "The list key (44 letters and digits). Send it as x-list-key."
},
"shareUrl": {
"type": "string",
"format": "uri",
"description": "Give this to the person; anyone who opens it is on the list."
},
"adminUrl": {
"type": "string",
"format": "uri",
"description": "Only for the list's owner: remove people, change the link, delete the list."
},
"next": {
"type": "string"
}
}
}
]
},
"example": {
"list": {
"id": "PG2nGY8htowKaWGc7qmv",
"name": "Groceries",
"kind": "grocery",
"url": "https://lists-free.openapps.xyz/l/PG2nGY8htowKaWGc7qmv"
},
"toGet": [
{
"id": "i0rkl7un1d1x3ht",
"name": "Chicken",
"quantity": "2 lb",
"note": null,
"section": "meat",
"sectionName": "Meat and seafood",
"checked": false
},
{
"id": "i0otvjk61s1cktm",
"name": "Eggs",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
},
{
"id": "i11w0wwy0lp762e",
"name": "Milk",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
}
],
"checkedOff": [],
"say": "Groceries has 3 items to get: 2 lb chicken, Eggs, Milk.",
"key": "PG2nGY8htowKaWGc7qmvDSecAULzKfzLCZCo3urSrNuK",
"shareUrl": "https://lists-free.openapps.xyz/l/PG2nGY8htowKaWGc7qmv#DSecAULzKfzLCZCo3urSrNuK",
"adminUrl": "https://lists-free.openapps.xyz/l/PG2nGY8htowKaWGc7qmv/admin#nfY3cduM4nKoDpd6VEpwBQZf"
}
}
}
},
"400": {
"description": "Bad name, kind or items",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/list": {
"get": {
"security": [
{
"listKey": []
}
],
"operationId": "readList",
"summary": "Read a list: what to get in store order, what is checked off, and a sentence to read out",
"responses": {
"200": {
"description": "The list",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ListSummary"
},
"example": {
"list": {
"id": "PG2nGY8htowKaWGc7qmv",
"name": "Groceries",
"kind": "grocery",
"url": "https://lists-free.openapps.xyz/l/PG2nGY8htowKaWGc7qmv"
},
"toGet": [
{
"id": "i0rkl7un1d1x3ht",
"name": "Chicken",
"quantity": "2 lb",
"note": null,
"section": "meat",
"sectionName": "Meat and seafood",
"checked": false
},
{
"id": "i0otvjk61s1cktm",
"name": "Eggs",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
},
{
"id": "i11w0wwy0lp762e",
"name": "Milk",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
}
],
"checkedOff": [],
"say": "Groceries has 3 items to get: 2 lb chicken, Eggs, Milk."
}
}
}
},
"401": {
"description": "No list key, or not 44 letters and digits",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "The key does not work; the link may have been changed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"410": {
"description": "The list was deleted",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/list/items": {
"post": {
"security": [
{
"listKey": []
}
],
"operationId": "addItems",
"summary": "Add items, from text (\"milk and eggs\") or a list",
"description": "Items already on the list are not added twice; checked-off items come back unchecked. A leading amount becomes the quantity (\"2 lb chicken\"). Up to 50 items per call.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AddItems"
},
"examples": {
"text": {
"value": {
"text": "milk and eggs, 2 lb chicken"
}
},
"items": {
"value": {
"items": [
"milk",
{
"name": "coffee",
"quantity": "1 bag",
"note": "decaf",
"section": "drinks"
}
]
}
}
}
}
}
},
"responses": {
"200": {
"description": "Nothing new: everything was already on the list",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/ListSummary"
},
{
"type": "object",
"properties": {
"added": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Item"
}
},
"alreadyOnList": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
]
},
"example": {
"list": {
"id": "PG2nGY8htowKaWGc7qmv",
"name": "Groceries",
"kind": "grocery",
"url": "https://lists-free.openapps.xyz/l/PG2nGY8htowKaWGc7qmv"
},
"toGet": [
{
"id": "i0rkl7un1d1x3ht",
"name": "Chicken",
"quantity": "2 lb",
"note": null,
"section": "meat",
"sectionName": "Meat and seafood",
"checked": false
},
{
"id": "i0otvjk61s1cktm",
"name": "Eggs",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
},
{
"id": "i11w0wwy0lp762e",
"name": "Milk",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
}
],
"checkedOff": [],
"say": "Groceries has 3 items to get: 2 lb chicken, Eggs, Milk."
}
}
}
},
"201": {
"description": "Items added",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/ListSummary"
},
{
"type": "object",
"properties": {
"added": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Item"
}
},
"alreadyOnList": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
]
},
"example": {
"list": {
"id": "PG2nGY8htowKaWGc7qmv",
"name": "Groceries",
"kind": "grocery",
"url": "https://lists-free.openapps.xyz/l/PG2nGY8htowKaWGc7qmv"
},
"toGet": [
{
"id": "i0rkl7un1d1x3ht",
"name": "Chicken",
"quantity": "2 lb",
"note": null,
"section": "meat",
"sectionName": "Meat and seafood",
"checked": false
},
{
"id": "i0otvjk61s1cktm",
"name": "Eggs",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
},
{
"id": "i11w0wwy0lp762e",
"name": "Milk",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
}
],
"checkedOff": [],
"say": "Groceries has 3 items to get: 2 lb chicken, Eggs, Milk."
}
}
}
},
"400": {
"description": "Nothing to add, too many items, or a bad section",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "No list key, or not 44 letters and digits",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "The key does not work; the link may have been changed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"410": {
"description": "The list was deleted",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/list/items/{item}": {
"parameters": [
{
"name": "item",
"in": "path",
"required": true,
"description": "The item's id, or its name (URL-encoded), like milk or olive%20oil",
"schema": {
"type": "string"
},
"example": "milk"
}
],
"patch": {
"security": [
{
"listKey": []
}
],
"operationId": "updateItem",
"summary": "Check off, uncheck, rename or change an item",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PatchItem"
},
"example": {
"checked": true
}
}
}
},
"responses": {
"200": {
"description": "The list after the change",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/ListSummary"
},
{
"type": "object",
"properties": {
"updated": {
"$ref": "#/components/schemas/Item"
}
}
}
]
},
"example": {
"list": {
"id": "PG2nGY8htowKaWGc7qmv",
"name": "Groceries",
"kind": "grocery",
"url": "https://lists-free.openapps.xyz/l/PG2nGY8htowKaWGc7qmv"
},
"toGet": [
{
"id": "i0rkl7un1d1x3ht",
"name": "Chicken",
"quantity": "2 lb",
"note": null,
"section": "meat",
"sectionName": "Meat and seafood",
"checked": false
},
{
"id": "i0otvjk61s1cktm",
"name": "Eggs",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
},
{
"id": "i11w0wwy0lp762e",
"name": "Milk",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
}
],
"checkedOff": [],
"say": "Groceries has 3 items to get: 2 lb chicken, Eggs, Milk."
}
}
}
},
"400": {
"description": "Nothing to change, a bad value, or an item path that is not URL-encoded correctly",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "No list key, or not 44 letters and digits",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "The key does not work; the link may have been changed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "That item is not on the list",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "Renaming onto an item already on the list",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"410": {
"description": "The list was deleted",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
},
"delete": {
"security": [
{
"listKey": []
}
],
"operationId": "removeItem",
"summary": "Remove an item from the list",
"responses": {
"200": {
"description": "The list after removing the item",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/ListSummary"
},
{
"type": "object",
"properties": {
"removed": {
"$ref": "#/components/schemas/Item"
}
}
}
]
},
"example": {
"list": {
"id": "PG2nGY8htowKaWGc7qmv",
"name": "Groceries",
"kind": "grocery",
"url": "https://lists-free.openapps.xyz/l/PG2nGY8htowKaWGc7qmv"
},
"toGet": [
{
"id": "i0rkl7un1d1x3ht",
"name": "Chicken",
"quantity": "2 lb",
"note": null,
"section": "meat",
"sectionName": "Meat and seafood",
"checked": false
},
{
"id": "i0otvjk61s1cktm",
"name": "Eggs",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
},
{
"id": "i11w0wwy0lp762e",
"name": "Milk",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
}
],
"checkedOff": [],
"say": "Groceries has 3 items to get: 2 lb chicken, Eggs, Milk."
}
}
}
},
"400": {
"description": "The item in the path is not URL-encoded correctly",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "No list key, or not 44 letters and digits",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "The key does not work; the link may have been changed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "That item is not on the list",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"410": {
"description": "The list was deleted",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/list/clear-checked": {
"post": {
"security": [
{
"listKey": []
}
],
"operationId": "clearChecked",
"summary": "Remove everything checked off",
"responses": {
"200": {
"description": "The list after clearing",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/ListSummary"
},
{
"type": "object",
"properties": {
"removed": {
"type": "integer"
}
}
}
]
},
"example": {
"list": {
"id": "PG2nGY8htowKaWGc7qmv",
"name": "Groceries",
"kind": "grocery",
"url": "https://lists-free.openapps.xyz/l/PG2nGY8htowKaWGc7qmv"
},
"toGet": [
{
"id": "i0rkl7un1d1x3ht",
"name": "Chicken",
"quantity": "2 lb",
"note": null,
"section": "meat",
"sectionName": "Meat and seafood",
"checked": false
},
{
"id": "i0otvjk61s1cktm",
"name": "Eggs",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
},
{
"id": "i11w0wwy0lp762e",
"name": "Milk",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
}
],
"checkedOff": [],
"say": "Groceries has 3 items to get: 2 lb chicken, Eggs, Milk."
}
}
}
},
"401": {
"description": "No list key, or not 44 letters and digits",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "The key does not work; the link may have been changed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"410": {
"description": "The list was deleted",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
}
},
"components": {
"securitySchemes": {
"listKey": {
"type": "apiKey",
"in": "header",
"name": "x-list-key",
"description": "The list key: 44 letters and digits, from POST /lists or the list's Share screen."
}
},
"schemas": {
"Error": {
"type": "object",
"required": [
"error"
],
"properties": {
"error": {
"type": "string",
"description": "A sentence that says what to change."
}
},
"example": {
"error": "Send the list key in the x-list-key header."
}
},
"Section": {
"type": "string",
"enum": [
"produce",
"bakery",
"deli",
"meat",
"dairy",
"frozen",
"pantry",
"baking",
"breakfast",
"snacks",
"drinks",
"household",
"personal",
"baby",
"pets",
"other"
]
},
"Item": {
"type": "object",
"required": [
"id",
"name",
"section",
"checked"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"quantity": {
"type": [
"string",
"null"
]
},
"note": {
"type": [
"string",
"null"
]
},
"section": {
"$ref": "#/components/schemas/Section"
},
"sectionName": {
"type": "string"
},
"checked": {
"type": "boolean"
}
},
"example": {
"id": "i11w0wwy0lp762e",
"name": "Milk",
"quantity": null,
"note": null,
"section": "dairy",
"sectionName": "Dairy and eggs",
"checked": false
}
},
"ListSummary": {
"type": "object",
"required": [
"list",
"toGet",
"checkedOff",
"say"
],
"properties": {
"list": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"grocery",
"todo"
]
},
"url": {
"type": "string",
"format": "uri"
}
}
},
"toGet": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Item"
},
"description": "Unchecked items, in store order (grocery) or the order added (todo)."
},
"checkedOff": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Item"
}
},
"say": {
"type": "string",
"description": "One sentence to read out."
}
}
},
"NewItem": {
"oneOf": [
{
"type": "string",
"description": "Like \"2 lb chicken\" or \"milk and eggs\"."
},
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"maxLength": 100
},
"quantity": {
"type": "string",
"maxLength": 40
},
"note": {
"type": "string",
"maxLength": 300
},
"section": {
"$ref": "#/components/schemas/Section"
}
}
}
]
},
"CreateList": {
"type": "object",
"properties": {
"name": {
"type": "string",
"maxLength": 60,
"default": "Groceries"
},
"kind": {
"type": "string",
"enum": [
"grocery",
"todo"
]
},
"items": {
"type": "array",
"maxItems": 50,
"items": {
"$ref": "#/components/schemas/NewItem"
}
},
"text": {
"type": "string"
}
}
},
"AddItems": {
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "Split on commas, new lines and \"and\"."
},
"items": {
"type": "array",
"maxItems": 50,
"items": {
"$ref": "#/components/schemas/NewItem"
}
}
}
},
"PatchItem": {
"type": "object",
"properties": {
"checked": {
"type": "boolean"
},
"name": {
"type": "string",
"maxLength": 100
},
"quantity": {
"type": "string",
"maxLength": 40
},
"note": {
"type": "string",
"maxLength": 300
},
"section": {
"$ref": "#/components/schemas/Section"
}
}
}
}
}
}Change history
Point OpenApps links to openapps.free
Bind agent address in policy constants
Use OpenApps service URLs and add contribution strip
Bounded source sync
Bounded source sync
Bounded source sync