API.md

Cooked Customer API Spec

This API lets a customer-owned integration log in, search and manage saved recipes, and manage the customer's shopping list.

Base URL: https://cooked.wiki

All JSON requests should use:

Content-Type: application/json
Accept: application/json

Authentication

The API uses a Cooked session cookie for authenticated requests. Treat this cookie like a password: store it only in a trusted secret store, never log it, and never ask users to paste it into public or untrusted chats.

Send the session cookie with every authenticated /api/... request:

Cookie: <session-cookie-name>=<session-cookie-value>

There are two supported ways to obtain a session cookie.

Username/Password Login

Use this flow only for accounts that have password login enabled.

POST /api/public/login

{
  "username": "customer_username",
  "password": "customer_password"
}

Success:

{
  "message": "Authenticated successfully",
  "username": "customer_username"
}

Invalid login returns 401 with code: "INVALID_AUTHENTICATION".

On success, store the session cookie from the response and send it on future authenticated requests.

Use this flow for social-login or passwordless accounts, such as accounts that log in with Google, Facebook, or Apple.

  1. Ask the user to log in normally at https://cooked.wiki/login in their browser.
  2. Ask the user to provide the Cooked session cookie for https://cooked.wiki through a trusted secret input.
  3. Store that cookie as a secret and send it as the Cookie header on API requests.

The cookie is a bearer credential. Anyone with it can act as the logged-in user until it expires or is invalidated.

Expired Sessions

If an authenticated API request returns 401, discard the stored session cookie.

If username/password credentials are configured, retry POST /api/public/login once and then retry the original request with the new cookie. If the account uses social login or no password is available, prompt the user to log in in the browser again and provide a fresh Cooked session cookie through a trusted secret input.

Recipes

Recipe content is plain Markdown-style recipe text. Use recipeId values returned by list/search/create calls.

Action Endpoint Notes
List recipes GET /api/user/{username}/recipes?page=1&page-count=30 Returns saved recipe cards.
Search recipes GET /api/user/{username}/recipes/search?q={query}&page=1 Searches the user's saved cookbook.
Get metadata GET /api/recipe/{recipeId}/metadata Title, image URLs, owner, edit permission.
Get content GET /api/recipe/{recipeId}/content Recipe body and portions.
Update content POST /api/recipe/{recipeId}/content Updates recipe body and portions.
Delete recipe DELETE /api/recipe/{recipeId} Removes a saved recipe.

Recipe Card

List and search return recipe cards like:

{
  "id": "recipe-id",
  "title": "Pasta with Tomato Sauce",
  "thumbnail-url": "https://..."
}

Recipe Content

GET /api/recipe/{recipeId}/content returns:

{
  "content": "# Pasta with Tomato Sauce\n\n- 200g pasta\n- 1 cup tomato sauce\n\n1. Boil the pasta.",
  "portions": 2
}

Update the recipe with:

{
  "description": "# Updated Recipe\n\n- ingredient\n\n1. step",
  "portions": 4
}

Success:

{
  "message": "Recipe updated",
  "code": "RECIPE_UPDATED"
}

Create Recipe From Text

Use this when the integration already has recipe text.

  1. Preview the recipe:

POST /api/new/from-text/preview

{
  "recipe-name": "Pasta with Tomato Sauce",
  "recipe-text": "Ingredients: 200g pasta, 1 cup tomato sauce. Steps: Boil pasta, warm sauce, combine."
}

The response includes title, markdown, and portions.

  1. Save the preview:

POST /api/recipe/import/save

{
  "title": "Pasta with Tomato Sauce",
  "description": "# Pasta with Tomato Sauce\n\n- 200g pasta\n- 1 cup tomato sauce\n\n1. Boil the pasta.",
  "portions": 2
}

Success:

{
  "recipe-id": "new-recipe-id"
}

Create Recipe From URL

Use this when the integration has a recipe URL.

POST /api/new

{
  "url": "https://example.com/recipe"
}

The response may include either a saved recipe-id or an import draft extraction-id.

When an extraction-id is returned:

  1. Read the draft with GET /api/recipe/{extractionId}/metadata and GET /api/recipe/{extractionId}/content.
  2. Save it with POST /api/extract/{extractionId}/save.

Save request:

{
  "description": "# Imported Recipe\n\n- ingredient\n\n1. step",
  "portions": 4
}

Success:

{
  "recipe-id": "new-recipe-id"
}

Shopping List

The API accepts plain-text ingredients and returns organized shopping-list product groups. Use product group IDs from GET /api/user/{username}/shopping-list for updates and removals.

Action Endpoint Notes
Add ingredients PUT /api/user/shopping-list Adds one or more ingredients to the logged-in user's list.
Get list GET /api/user/{username}/shopping-list Returns aisles and product groups.
Update item PUT /api/user/{username}/shopping-list/product-groups/{productGroupId} Updates name, quantity, aisle, or selected state.
Mark selected PUT /api/user/{username}/shopping-list/product-groups/selection Replaces the complete selected/purchased set.
Remove items DELETE /api/user/{username}/shopping-list/product-groups Removes product groups by ID.
Clear list DELETE /api/user/{username}/shopping-list Clears all shopping-list items.

Add Ingredients

{
  "ingredients": "200g pasta\n1 cup tomato sauce\nParmesan cheese",
  "recipe-id": "optional-recipe-id"
}

recipe-id is optional and may be a saved recipe ID or an import draft ID.

Success:

{
  "success": true,
  "added-count": 3,
  "ingredients": [
    "200g pasta",
    "1 cup tomato sauce",
    "Parmesan cheese"
  ]
}

Shopping List Response

{
  "success": true,
  "username": "customer_username",
  "shopping-list": {
    "aisles": [
      {
        "aisle-id": "pantry",
        "aisle-name": "Pantry",
        "product-groups": [
          {
            "id": "product-group-id",
            "name": "Pasta",
            "quantity": "200g",
            "selected": false
          }
        ]
      }
    ]
  },
  "recipes": [
    "recipe-id"
  ]
}

Update Shopping List Item

Send all editable fields:

{
  "name": "Pasta",
  "quantity": "250g",
  "aisle-id": "pantry",
  "selected": false
}

Success:

{
  "success": true
}

Mark Purchased Items

selected is the full list of product group IDs that should be marked selected/purchased. Items not included are marked unselected.

{
  "selected": [
    "product-group-id-1",
    "product-group-id-2"
  ]
}

Remove Shopping List Items

{
  "ids": [
    "product-group-id-1",
    "product-group-id-2"
  ]
}

Common Errors

Status Meaning
400 Invalid or missing request data.
401 Not logged in or invalid credentials.
403 Logged in but not allowed to edit this resource.
404 Recipe, draft, or shopping-list item not found.

Example:

{
  "message": "Recipe not found",
  "code": "RECIPE_NOT_FOUND"
}