{
  "openapi": "3.1.0",
  "info": {
    "title": "Bay of Ideas public read API",
    "version": "1.1.0",
    "summary": "Anonymous, read-only access to the public ideas, groups, blog and statistics of bayofideas.com.",
    "description": "Bay of Ideas is the open registry where ideas are matched with the competences they lack. This document covers only the anonymous read endpoints. Responses show members by first name only. Consumers must ignore unknown keys. Please identify your client in the User-Agent and keep to a few requests per second; see https://bayofideas.com/agents.",
    "contact": {
      "name": "Bay of Ideas",
      "url": "https://bayofideas.com/contact"
    }
  },
  "servers": [
    {
      "url": "https://bayofideas.com/api"
    }
  ],
  "tags": [
    {
      "name": "Ideas"
    },
    {
      "name": "Groups"
    },
    {
      "name": "Blog"
    },
    {
      "name": "Lookup"
    },
    {
      "name": "Stats"
    }
  ],
  "paths": {
    "/ideas": {
      "get": {
        "operationId": "listIdeas",
        "tags": [
          "Ideas"
        ],
        "summary": "List public ideas",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Zero-based page number."
          },
          {
            "name": "size",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Page size, at most 100 (larger values are clamped)."
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Text search in name, short and long description and tags."
          },
          {
            "name": "categoryId",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Only ideas in this category (GET /lookup/categories)."
          },
          {
            "name": "competenceId",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              },
              "maxItems": 20
            },
            "description": "Only ideas looking for any of these competences (GET /lookup/competences). Repeat the parameter or separate with commas.",
            "style": "form",
            "explode": true
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "recent",
                "newest",
                "popular",
                "discussed"
              ],
              "default": "recent"
            },
            "description": "recent: newest created first. newest: newest published first (use this for feeds and exports). popular: most liked and followed. discussed: most commented."
          },
          {
            "name": "aiVisible",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "true: only ideas whose owners allow AI assistants to use them (aiAccess). Agents, feeds and bulk exports should send true."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of public ideas.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IdeaPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/ideas/{id}": {
      "get": {
        "operationId": "getIdea",
        "tags": [
          "Ideas"
        ],
        "summary": "One public idea",
        "description": "id is the hashedId or any unique prefix of at least 8 hex characters (the shortId).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{8,32}$"
            },
            "description": "hashedId or shortId.",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "The idea, with its links.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Idea"
                }
              }
            }
          },
          "404": {
            "description": "Unknown, or not visible to anonymous callers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/ideas/boosted": {
      "get": {
        "operationId": "boostedIdeas",
        "tags": [
          "Ideas"
        ],
        "summary": "Ideas currently boosted, in random order",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 6,
              "default": 3
            },
            "description": "How many (clamped to 1-6)."
          }
        ],
        "responses": {
          "200": {
            "description": "Boosted public ideas.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Idea"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/groups": {
      "get": {
        "operationId": "listGroups",
        "tags": [
          "Groups"
        ],
        "summary": "List public groups",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Zero-based page number."
          },
          {
            "name": "size",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Page size, at most 100 (larger values are clamped)."
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Text search in name, short and long description and tags."
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "recent",
                "newest",
                "popular",
                "discussed"
              ],
              "default": "recent"
            },
            "description": "recent: newest created first. newest: newest published first (use this for feeds and exports). popular: most liked and followed. discussed: most commented."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of public groups (ideaTypeId 1).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IdeaPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/groups/{id}": {
      "get": {
        "operationId": "getGroup",
        "tags": [
          "Groups"
        ],
        "summary": "One public group",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{32}$"
            },
            "description": "The group's hashedId.",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "The group.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Idea"
                }
              }
            }
          },
          "404": {
            "description": "Unknown, or not visible to anonymous callers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/groups/{id}/ideas": {
      "get": {
        "operationId": "getGroupIdeas",
        "tags": [
          "Groups"
        ],
        "summary": "Public ideas linked to a group",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{32}$"
            },
            "description": "The group's hashedId.",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "At most 200, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Idea"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown, or not visible to anonymous callers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/blog": {
      "get": {
        "operationId": "listBlogPosts",
        "tags": [
          "Blog"
        ],
        "summary": "Published blog posts",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Zero-based page number."
          },
          {
            "name": "size",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Page size, at most 100 (larger values are clamped)."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of posts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BlogPage"
                }
              }
            }
          }
        }
      }
    },
    "/blog/{slug}": {
      "get": {
        "operationId": "getBlogPost",
        "tags": [
          "Blog"
        ],
        "summary": "One published blog post",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "description": "The post's slug.",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "The post.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BlogPost"
                }
              }
            }
          },
          "404": {
            "description": "Unknown, or not visible to anonymous callers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/lookup/competences": {
      "get": {
        "operationId": "listCompetences",
        "tags": [
          "Lookup"
        ],
        "summary": "All competences",
        "responses": {
          "200": {
            "description": "Competences, names in Swedish, group names in English.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Competence"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/lookup/categories": {
      "get": {
        "operationId": "listCategories",
        "tags": [
          "Lookup"
        ],
        "summary": "All idea categories",
        "responses": {
          "200": {
            "description": "Categories, names in Swedish.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Lookup"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/stats/public": {
      "get": {
        "operationId": "publicStats",
        "tags": [
          "Stats"
        ],
        "summary": "Statistics over public ideas",
        "description": "Cached for 10 minutes.",
        "responses": {
          "200": {
            "description": "The statistics.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicStats"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Idea": {
        "type": "object",
        "description": "An idea or a group (ideaTypeId 1), as an anonymous caller sees it. Consumers must ignore unknown keys.",
        "required": [
          "hashedId",
          "ideaTypeId",
          "name",
          "competences",
          "categoryIds",
          "likesCount",
          "commentsCount",
          "followersCount"
        ],
        "properties": {
          "hashedId": {
            "type": "string",
            "pattern": "^[0-9a-f]{32}$",
            "description": "The idea's id in the API."
          },
          "shortId": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9a-f]{8,32}$",
            "description": "Shortest unique prefix of hashedId (at least 8 chars). GET /ideas/{shortId} answers with this idea. Missing on older API versions: use hashedId."
          },
          "slug": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
            "maxLength": 60,
            "description": "Readable URL part from the name. Canonical page: https://bayofideas.com/idea/{slug}-{shortId}"
          },
          "language": {
            "enum": [
              "sv",
              "en",
              null
            ],
            "description": "Language guessed from the public text; null when unsure."
          },
          "ideaTypeId": {
            "type": "integer",
            "enum": [
              0,
              1
            ],
            "description": "0 idea, 1 group."
          },
          "name": {
            "type": "string",
            "maxLength": 90
          },
          "descriptionShort": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 90,
            "description": "One-line pitch."
          },
          "descriptionLong": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1500
          },
          "descriptionPublic": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1000
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "The fullest text this caller may read (public tier for anonymous callers)."
          },
          "descriptionNetwork": {
            "type": "null",
            "description": "Members' tier: always null for anonymous callers."
          },
          "descriptionConnected": {
            "type": "null",
            "description": "Team tier: always null for anonymous callers."
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "description": "Image file name; the file is served at https://bayofideas.com/images/ideas/{image}."
          },
          "tags": {
            "type": [
              "string",
              "null"
            ],
            "description": "Space-separated tags."
          },
          "anonymous": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Posted anonymously: creator is null."
          },
          "visibilityId": {
            "type": "integer",
            "description": "1 = public. Anonymous callers only see public ideas."
          },
          "statusId": {
            "type": [
              "integer",
              "null"
            ],
            "description": "1 active (anonymous callers only see active ideas)."
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "Published",
              "Draft",
              null
            ]
          },
          "published": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?$",
            "description": "When it was published. Server local time without offset (ISO 8601, e.g. 2026-10-18T15:37:28); the site runs in UTC."
          },
          "created": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?$",
            "description": "When it was created. Server local time without offset (ISO 8601, e.g. 2026-10-18T15:37:28); the site runs in UTC."
          },
          "updated": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Last change of a published idea, an ISO 8601 instant (UTC, Z)."
          },
          "createdByHashedId": {
            "type": "null",
            "description": "Always null for anonymous callers."
          },
          "creator": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PublicPerson"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who started it: first name only. Null for anonymous ideas."
          },
          "categoryIds": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "See GET /lookup/categories."
          },
          "competences": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Competence"
            },
            "description": "Competences the idea is looking for."
          },
          "links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IdeaLink"
            },
            "description": "Filled on the single idea and group only; empty in lists."
          },
          "likesCount": {
            "type": "integer",
            "minimum": 0
          },
          "commentsCount": {
            "type": "integer",
            "minimum": 0
          },
          "followersCount": {
            "type": "integer",
            "minimum": 0
          },
          "liked": {
            "type": "null",
            "description": "Signed-in callers only."
          },
          "following": {
            "type": "null",
            "description": "Signed-in callers only."
          },
          "connectionStatus": {
            "type": "null",
            "description": "Signed-in callers only."
          },
          "aiAccess": {
            "type": "boolean",
            "description": "The owner's AI visibility switch (default true). Agents, feeds and bulk exports should skip ideas with false; consumers treat a missing value as true."
          }
        }
      },
      "IdeaPage": {
        "type": "object",
        "description": "A page of ideas or groups.",
        "required": [
          "content",
          "page",
          "size",
          "totalElements",
          "totalPages"
        ],
        "properties": {
          "content": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Idea"
            }
          },
          "page": {
            "type": "integer",
            "minimum": 0
          },
          "size": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          },
          "totalElements": {
            "type": "integer",
            "minimum": 0
          },
          "totalPages": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "PublicPerson": {
        "type": "object",
        "description": "A member as anonymous callers see them: the first name and nothing else (no id, no profile picture).",
        "required": [
          "firstName"
        ],
        "properties": {
          "firstName": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Competence": {
        "type": "object",
        "required": [
          "competenceId",
          "name"
        ],
        "properties": {
          "competenceId": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "description": "As stored (Swedish)."
          },
          "competenceGroupId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "groupName": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "IdeaLink": {
        "type": "object",
        "required": [
          "ideaLinkId"
        ],
        "properties": {
          "ideaLinkId": {
            "type": "integer"
          },
          "linkName": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "BlogPost": {
        "type": "object",
        "required": [
          "blogPostId",
          "title",
          "slug"
        ],
        "properties": {
          "blogPostId": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "body": {
            "type": [
              "string",
              "null"
            ],
            "description": "HTML."
          },
          "image": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "pressRelease": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "authorFirstName": {
            "type": [
              "string",
              "null"
            ]
          },
          "authorLastName": {
            "type": [
              "string",
              "null"
            ]
          },
          "published": {
            "type": [
              "string",
              "null"
            ],
            "description": "Server local time without offset (ISO 8601, e.g. 2026-10-18T15:37:28); the site runs in UTC."
          },
          "created": {
            "type": [
              "string",
              "null"
            ],
            "description": "Server local time without offset (ISO 8601, e.g. 2026-10-18T15:37:28); the site runs in UTC."
          }
        }
      },
      "BlogPage": {
        "type": "object",
        "description": "A page of published blog posts, newest first.",
        "required": [
          "content",
          "page",
          "size",
          "totalElements",
          "totalPages"
        ],
        "properties": {
          "content": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BlogPost"
            }
          },
          "page": {
            "type": "integer",
            "minimum": 0
          },
          "size": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          },
          "totalElements": {
            "type": "integer",
            "minimum": 0
          },
          "totalPages": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "Lookup": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "description": "As stored (Swedish)."
          }
        }
      },
      "PublicStats": {
        "type": "object",
        "description": "Aggregates over public, published ideas. Consumers ignore unknown keys.",
        "required": [
          "asOf",
          "publicIdeas",
          "ideasByYear",
          "byLanguage",
          "topCompetencesNeeded",
          "byCategory",
          "ideasNamingCompetences",
          "ideasWithTeam",
          "ideasWithComments",
          "members"
        ],
        "properties": {
          "asOf": {
            "type": "string",
            "format": "date-time"
          },
          "publicIdeas": {
            "type": "integer",
            "minimum": 0
          },
          "ideasByYear": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "year",
                "count"
              ],
              "properties": {
                "year": {
                  "type": "integer"
                },
                "count": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "byLanguage": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "language",
                "count"
              ],
              "properties": {
                "language": {
                  "enum": [
                    "sv",
                    "en",
                    null
                  ]
                },
                "count": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "topCompetencesNeeded": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "object",
              "required": [
                "competenceId",
                "name",
                "ideas"
              ],
              "properties": {
                "competenceId": {
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                },
                "ideas": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "byCategory": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "categoryId",
                "name",
                "ideas"
              ],
              "properties": {
                "categoryId": {
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                },
                "ideas": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "ideasNamingCompetences": {
            "type": "integer",
            "minimum": 0
          },
          "ideasWithTeam": {
            "type": "integer",
            "minimum": 0
          },
          "ideasWithComments": {
            "type": "integer",
            "minimum": 0
          },
          "members": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "status",
          "message"
        ],
        "properties": {
          "status": {
            "type": "integer"
          },
          "message": {
            "type": "string"
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ]
          },
          "traceId": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      }
    }
  }
}
