Skip to content

Rest API

PanSalut edited this page Jan 24, 2026 · 8 revisions

REST API

Koffan provides a REST API for programmatic access to shopping lists. This is especially useful if you want to migrate from another service to Koffan.

Tip: Share this documentation with an AI assistant (like ChatGPT or Claude) and ask it to help you migrate your data. The AI can read your export file and generate the right API calls to import everything into Koffan.

Overview

  • Base URL: /api/v1
  • Authentication: Bearer token
  • Content-Type: application/json

Quick start: Use the Batch endpoint to create a complete shopping list with sections and items in a single request!

Enabling the API

The API is disabled by default. To enable it, set the API_TOKEN environment variable:

API_TOKEN=your-secret-token

Security note: The API has no rate limiting. Use a strong, random token and keep it secret.

Authentication

All requests require a Bearer token in the Authorization header:

Authorization: Bearer your-secret-token

Authentication Errors

Error Code Status Description
api_disabled 503 API_TOKEN not configured
missing_token 401 No Authorization header
invalid_format 401 Wrong format (not "Bearer token")
invalid_token 401 Token doesn't match API_TOKEN

Lists

Get All Lists

GET /api/v1/lists

Response:

{
  "lists": [
    {
      "id": 1,
      "name": "Shopping",
      "icon": "🛒",
      "order": 1,
      "stats": {
        "total_items": 10,
        "completed_items": 3
      }
    }
  ]
}

Get Single List

GET /api/v1/lists/:id

Create List

POST /api/v1/lists

Request:

{
  "name": "Weekly Shopping",
  "icon": "cart"
}
Field Type Required Max Length
name string Yes 100
icon string No 20 (emoji or alias)

Available icons:

You can use either emoji directly or string aliases:

Icon Emoji String aliases
🛒 "🛒" "cart", "shopping"
🏠 "🏠" "home", "house"
🎁 "🎁" "gift", "present"
🎄 "🎄" "christmas", "xmas"
🎂 "🎂" "birthday", "cake"
🍕 "🍕" "food", "pizza"
🥗 "🥗" "salad", "healthy"
💊 "💊" "medicine", "health", "pills"
🐕 "🐕" "pet", "pets", "dog"
🧹 "🧹" "cleaning", "clean"
📦 "📦" "package", "packages", "box"
✈️ "✈️" "travel", "trip", "flight"
🏋️ "🏋️" "fitness", "gym", "workout"
📚 "📚" "books", "book", "reading"
🛠️ "🛠️" "tools", "tool"
💼 "💼" "work", "office", "business"

Update List

PUT /api/v1/lists/:id

Request:

{
  "name": "New Name",
  "icon": "🏠"
}

Delete List

DELETE /api/v1/lists/:id

Response: 204 No Content

Get List Sections

GET /api/v1/lists/:id/sections

Response:

{
  "sections": [
    {
      "id": 1,
      "list_id": 1,
      "name": "Produce",
      "order": 1,
      "items": []
    }
  ]
}

Reorder List

POST /api/v1/lists/:id/move-up
POST /api/v1/lists/:id/move-down

Sections

Get Single Section

GET /api/v1/sections/:id

Create Section

POST /api/v1/sections

Request:

{
  "list_id": 1,
  "name": "Produce"
}
Field Type Required Max Length
list_id int Yes -
name string Yes 100

Update Section

PUT /api/v1/sections/:id

Request:

{
  "name": "Vegetables"
}

Delete Section

DELETE /api/v1/sections/:id

Get Section Items

GET /api/v1/sections/:id/items

Response:

{
  "items": [
    {
      "id": 1,
      "section_id": 1,
      "name": "Apples",
      "description": "Red apples",
      "quantity": 2,
      "completed": false,
      "uncertain": false,
      "order": 1
    }
  ]
}

Reorder Section

POST /api/v1/sections/:id/move-up
POST /api/v1/sections/:id/move-down

Items

Get Single Item

GET /api/v1/items/:id

Create Item

POST /api/v1/items

Request:

{
  "section_id": 1,
  "name": "Apples",
  "description": "Red apples",
  "quantity": 2
}
Field Type Required Max Length
section_id int Yes -
name string Yes 200
description string No 500
quantity int No - (default: 0)

Update Item

PUT /api/v1/items/:id

Request:

{
  "name": "Green Apples",
  "description": "Granny Smith",
  "quantity": 3,
  "completed": true,
  "uncertain": false
}

Note: All fields are optional. Only provided fields will be updated. If quantity is not provided, the existing value is preserved.

Delete Item

DELETE /api/v1/items/:id

Toggle Item Completed

POST /api/v1/items/:id/toggle

Toggle Item Uncertain

POST /api/v1/items/:id/uncertain

Move Item to Another Section

POST /api/v1/items/:id/move

Request:

{
  "section_id": 2
}

Reorder Item

POST /api/v1/items/:id/move-up
POST /api/v1/items/:id/move-down

Batch Operations

The most powerful endpoint! Create complete shopping lists with sections and items in a single API call.

POST /api/v1/batch

Why use Batch?

  • One request - Create a list, multiple sections, and dozens of items at once
  • Atomic - All-or-nothing transaction: everything succeeds or nothing changes
  • Efficient - No need to chain multiple API calls and track IDs
  • Flexible - Three modes: create new list, add to existing list, or add to existing section

Variant 1: Create New List with Sections and Items

Create a complete shopping list structure from scratch:

{
  "list": {
    "name": "Weekly Shopping",
    "icon": "🛒",
    "sections": [
      {
        "name": "Produce",
        "items": [
          {"name": "Apples", "description": "Red", "quantity": 2},
          {"name": "Bananas", "quantity": 6}
        ]
      },
      {
        "name": "Dairy",
        "items": [
          {"name": "Milk", "description": "2L", "quantity": 1}
        ]
      }
    ]
  }
}

Variant 2: Add Sections to Existing List

Expand an existing list with new sections and items (use list_id from a previous response):

{
  "list_id": 1,
  "sections": [
    {
      "name": "Frozen",
      "items": [
        {"name": "Ice Cream"}
      ]
    }
  ]
}

Variant 3: Add Items to Existing Section

Quickly add multiple items to a section (use section_id from a previous response):

{
  "section_id": 1,
  "items": [
    {"name": "Oranges", "description": "Fresh", "quantity": 4},
    {"name": "Grapes", "quantity": 1}
  ]
}

Response (contains only what was created):

{
  "list": { ... },      // only in Variant 1
  "sections": [ ... ],  // in Variant 1 and 2
  "items": [ ... ]      // always present
}

History (Suggestions)

History entries are used for autocomplete suggestions when adding items.

Get All History

GET /api/v1/history

Response:

{
  "items": [
    {
      "id": 1,
      "name": "Apples",
      "section_id": 1,
      "usage_count": 5
    }
  ]
}

Add History Entry

POST /api/v1/history

Request:

{
  "name": "Oranges",
  "section_id": 1
}
Field Type Required Max Length
name string Yes 200
section_id int No -

Delete History Entry

DELETE /api/v1/history/:id

Batch Delete History

POST /api/v1/history/batch-delete

Request:

{
  "ids": [1, 2, 3]
}

Response:

{
  "deleted": 3
}

Error Responses

All errors follow this format:

{
  "error": "error_code",
  "message": "Human readable message"
}

Common Error Codes

Error Code Description
invalid_json Failed to parse request body
validation_error Validation constraint violated
not_found Resource doesn't exist
db_error Database operation failed
create_failed Failed to create resource
update_failed Failed to update resource
delete_failed Failed to delete resource
move_failed Reordering operation failed
invalid_id ID parameter is not valid

HTTP Status Codes

Status Description
200 Success (GET, PUT, POST actions)
201 Created (POST creating new resource)
204 No Content (DELETE)
400 Bad Request (validation error)
401 Unauthorized (auth failed)
404 Not Found
503 Service Unavailable (API disabled)
500 Internal Server Error

Example: Complete Workflow

# Set token
TOKEN="Bearer your-secret-token"

# Create a list with sections and items (with quantities)
curl -X POST http://localhost:3000/api/v1/batch \
  -H "Authorization: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "list": {
      "name": "Grocery",
      "icon": "🛒",
      "sections": [
        {
          "name": "Fruits",
          "items": [
            {"name": "Apples", "quantity": 4},
            {"name": "Bananas", "quantity": 6}
          ]
        }
      ]
    }
  }'

# Toggle item as completed
curl -X POST http://localhost:3000/api/v1/items/1/toggle \
  -H "Authorization: $TOKEN"

# Get all lists
curl http://localhost:3000/api/v1/lists \
  -H "Authorization: $TOKEN"

Clone this wiki locally