Skip to content

API Reference

Complete API reference for the Yappa Knowledge Hub backend.

Base URL

http://localhost:8000/api

In production, replace with your actual domain.

Content Type

All requests and responses use JSON:

Content-Type: application/json

Authentication

Currently, the API does not require authentication. All endpoints are publicly accessible.

Future Enhancement: JWT or API key authentication will be added.


Knowledge Endpoints

Create Knowledge Item

Creates a new knowledge item in both SQLite and Notion.

Endpoint: POST /api/knowledge

Request Body:

json
{
  "title": "How to Deploy Symfony Apps",
  "content": "Detailed deployment guide with step-by-step instructions...",
  "tags": ["symfony", "deployment", "devops"],
  "category": "notion-category-id",
  "categoryId": "notion-category-id",
  "targetGroups": ["developers", "devops"],
  "status": "Not started",
  "url": "https://example.com/article",
  "userId": "U12345",
  "sourceMessage": {
    "ts": "1234567890.123456",
    "channel": "C12345"
  }
}

Request Fields:

  • title (string, required) - Knowledge item title
  • content (string, optional) - Full content/description
  • tags (array, optional) - Array of tag strings
  • category / categoryId (string, optional) - Notion category page ID
  • targetGroups (array, optional) - Target groups (inherits from category if not provided)
  • status (string, optional) - Status (default: "Not started")
  • url (string, optional) - Source URL
  • userId (string, optional) - User ID (Slack user ID)
  • sourceMessage (object, optional) - Original Slack message metadata

Response: 201 Created

json
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "notionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "notionUrl": "https://www.notion.so/workspace/page-title-a1b2c3d4e5f67890abcdef1234567890",
  "title": "How to Deploy Symfony Apps",
  "content": "Detailed deployment guide with step-by-step instructions...",
  "tags": ["symfony", "deployment", "devops"],
  "targetGroups": ["developers", "devops"],
  "status": "Not started",
  "sourceUrl": "https://example.com/article",
  "aiSummary": "",
  "created": "2026-02-19T10:30:00.000Z",
  "lastEdited": "2026-02-19T10:30:00.000Z",
  "categoryId": ["notion-category-id"]
}

Example:

bash
curl -X POST http://localhost:8000/api/knowledge \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Symfony Best Practices",
    "content": "A comprehensive guide...",
    "tags": ["symfony", "php"],
    "targetGroups": ["developers"]
  }'

List Knowledge Items

Lists all knowledge items or filters by search/category.

Endpoint: GET /api/knowledge

Query Parameters:

  • search (string, optional) - Full-text search query
  • category (string, optional) - Filter by category ID

Examples:

bash
# List all
GET /api/knowledge

# Search
GET /api/knowledge?search=symfony

# Filter by category
GET /api/knowledge?category=notion-category-id

Response: 200 OK

json
[
  {
    "id": "notion-page-id-1",
    "notionId": "notion-page-id-1",
    "notionUrl": "https://www.notion.so/...",
    "title": "Symfony Best Practices",
    "content": "Comprehensive guide...",
    "tags": ["symfony", "php"],
    "targetGroups": ["developers"],
    "status": "Done",
    "sourceUrl": "https://symfony.com/doc",
    "aiSummary": "This guide covers...",
    "created": "2026-02-19T10:30:00.000Z",
    "lastEdited": "2026-02-19T11:45:00.000Z",
    "categoryId": ["category-id"]
  },
  {
    "id": "notion-page-id-2",
    "title": "Docker Deployment",
    "content": "Docker deployment guide...",
    "tags": ["docker", "deployment"],
    "targetGroups": ["devops"],
    "status": "In progress",
    "created": "2026-02-18T15:20:00.000Z"
  }
]

Get Knowledge Item

Retrieves a single knowledge item by ID.

Endpoint: GET /api/knowledge/{id}

Path Parameters:

  • id (string, required) - Notion page ID

Response: 200 OK

json
{
  "id": "notion-page-id",
  "notionId": "notion-page-id",
  "notionUrl": "https://www.notion.so/...",
  "title": "Symfony Best Practices",
  "content": "Detailed content...",
  "tags": ["symfony", "php"],
  "targetGroups": ["developers"],
  "status": "Done",
  "sourceUrl": "https://example.com",
  "aiSummary": "This article covers...",
  "created": "2026-02-19T10:30:00.000Z",
  "lastEdited": "2026-02-19T11:45:00.000Z",
  "categoryId": ["notion-category-id"]
}

Error Response: 404 Not Found

json
{
  "error": "Knowledge item not found"
}

Example:

bash
curl http://localhost:8000/api/knowledge/a1b2c3d4-e5f6-7890-abcd-ef1234567890

Update Knowledge Item

Updates an existing knowledge item.

Endpoint: PUT /api/knowledge/{id} or PATCH /api/knowledge/{id}

Path Parameters:

  • id (string, required) - Notion page ID

Request Body (partial updates allowed):

json
{
  "title": "Updated Title",
  "content": "Updated content...",
  "tags": ["new", "tags"],
  "targetGroups": ["updated-groups"],
  "status": "Done",
  "category": "new-category-id"
}

Response: 200 OK

json
{
  "id": "notion-page-id",
  "title": "Updated Title",
  "content": "Updated content...",
  "tags": ["new", "tags"],
  "targetGroups": ["updated-groups"],
  "status": "Done",
  "lastEdited": "2026-02-19T12:00:00.000Z"
}

Example:

bash
curl -X PUT http://localhost:8000/api/knowledge/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "Content-Type: application/json" \
  -d '{"status": "Done"}'

Delete Knowledge Item

Archives a knowledge item (Notion doesn't support true deletion).

Endpoint: DELETE /api/knowledge/{id}

Path Parameters:

  • id (string, required) - Notion page ID

Response: 200 OK

json
{
  "message": "Knowledge archived successfully"
}

Example:

bash
curl -X DELETE http://localhost:8000/api/knowledge/a1b2c3d4-e5f6-7890-abcd-ef1234567890

Search Knowledge Items

Alternative search endpoint using POST.

Endpoint: POST /api/knowledge/search

Request Body:

json
{
  "query": "symfony deployment",
  "q": "symfony deployment"
}

Response: 200 OK

json
[
  {
    "id": "notion-page-id",
    "title": "Symfony Deployment Guide",
    "content": "...",
    "tags": ["symfony", "deployment"]
  }
]

Local Knowledge Endpoints

Read-only endpoints that query SQLite directly (faster, no Notion API calls).

List Local Knowledge Items

Endpoint: GET /api/local/knowledge

Response: 200 OK

json
[
  {
    "id": 1,
    "title": "Local Knowledge Item",
    "content": "Content stored in SQLite...",
    "tags": ["tag1", "tag2"],
    "category": 5,
    "targetGroups": ["developers"],
    "url": "https://example.com",
    "urlMetadata": {
      "title": "Page Title",
      "description": "Page description"
    },
    "status": "Not started",
    "createdAt": "2026-02-19T10:30:00+00:00"
  }
]

Note: Returns SQLite integer IDs, not Notion UUIDs.


List Local Categories

Endpoint: GET /api/local/categories

Response: 200 OK

json
[
  {
    "id": 1,
    "name": "Development",
    "description": "Development resources",
    "icon": "",
    "defaultTargetGroups": ["developers", "devops"],
    "isActive": true
  }
]

Category Endpoints

Create Category

Creates a new category in Notion.

Endpoint: POST /api/categories

Request Body:

json
{
  "name": "New Category",
  "description": "Category description",
  "icon": ":rocket:",
  "targetGroups": ["developers", "designers"],
  "defaultTargetGroups": ["developers", "designers"]
}

Request Fields:

  • name (string, required) - Category name
  • description (string, optional) - Category description
  • icon (string, optional) - Emoji icon (Slack format :emoji: or Unicode)
  • targetGroups / defaultTargetGroups (array, optional) - Default target groups

Response: 201 Created

json
{
  "id": "notion-page-id",
  "name": "New Category",
  "description": "Category description",
  "icon": "",
  "defaultTargetGroups": ["developers", "designers"],
  "color": null,
  "isActive": true,
  "sortOrder": 0,
  "digestFrequency": null,
  "lastDigestAt": null,
  "created": "2026-02-19T10:30:00.000Z",
  "updated": "2026-02-19T10:30:00.000Z"
}

Example:

bash
curl -X POST http://localhost:8000/api/categories \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Marketing",
    "description": "Marketing resources",
    "icon": "",
    "targetGroups": ["marketing"]
  }'

List Categories

Lists all categories with optional auto-sync from Notion.

Endpoint: GET /api/categories

Query Parameters:

  • sync (boolean, default: true) - Enable auto-sync from Notion

Examples:

bash
# With auto-sync (default)
GET /api/categories

# Without auto-sync (faster)
GET /api/categories?sync=false

Response: 200 OK

json
[
  {
    "id": "notion-page-id-1",
    "name": "Development",
    "description": "Development resources",
    "icon": "",
    "defaultTargetGroups": ["developers"],
    "isActive": true,
    "sortOrder": 1,
    "created": "2026-02-19T10:30:00+00:00",
    "updated": "2026-02-19T11:00:00+00:00"
  },
  {
    "id": "notion-page-id-2",
    "name": "Marketing",
    "description": "Marketing materials",
    "icon": "",
    "defaultTargetGroups": ["marketing"],
    "isActive": true,
    "sortOrder": 2,
    "created": "2026-02-18T09:00:00+00:00",
    "updated": "2026-02-18T09:00:00+00:00"
  }
]

Note: Auto-syncs from Notion if last sync was >5 minutes ago.


Get Category

Retrieves a single category by ID.

Endpoint: GET /api/categories/{id}

Path Parameters:

  • id (string, required) - Notion page ID

Response: 200 OK

json
{
  "id": "notion-page-id",
  "name": "Development",
  "description": "Development resources",
  "icon": "",
  "defaultTargetGroups": ["developers", "devops"],
  "color": "blue",
  "isActive": true,
  "sortOrder": 1,
  "digestFrequency": "weekly",
  "lastDigestAt": "2026-02-15T10:00:00.000Z",
  "created": "2026-01-01T00:00:00.000Z",
  "updated": "2026-02-19T10:30:00.000Z"
}

Update Category

Updates an existing category.

Endpoint: PUT /api/categories/{id} or PATCH /api/categories/{id}

Path Parameters:

  • id (string, required) - Notion page ID

Request Body (partial updates allowed):

json
{
  "name": "Updated Name",
  "description": "Updated description",
  "icon": "",
  "targetGroups": ["developers", "designers", "product"]
}

Response: 200 OK

json
{
  "id": "notion-page-id",
  "name": "Updated Name",
  "description": "Updated description",
  "icon": "",
  "defaultTargetGroups": ["developers", "designers", "product"],
  "updated": "2026-02-19T12:00:00.000Z"
}

Error Response: 500 Internal Server Error

json
{
  "error": "Failed to update category",
  "message": "Notion API error: ..."
}

Sync Categories

Manually triggers category sync from Notion.

Endpoint: POST /api/categories/sync

Response: 200 OK

json
{
  "message": "Categories synced successfully",
  "count": 15
}

Error Response: 500 Internal Server Error

json
{
  "error": "Sync failed",
  "message": "Notion API error: ..."
}

Example:

bash
curl -X POST http://localhost:8000/api/categories/sync

Error Responses

Standard Error Format

All errors follow this format:

json
{
  "error": "Error type",
  "message": "Detailed error message"
}

HTTP Status Codes

  • 200 OK - Successful GET, PUT, PATCH, DELETE
  • 201 Created - Successful POST (resource created)
  • 204 No Content - Successful DELETE with no response body
  • 400 Bad Request - Invalid request data
  • 404 Not Found - Resource not found
  • 500 Internal Server Error - Server error

Common Errors

400 Bad Request

json
{
  "error": "Invalid request",
  "message": "Missing required field: title"
}

404 Not Found

json
{
  "error": "Not found",
  "message": "Knowledge item with ID 'xyz' not found"
}

500 Internal Server Error

json
{
  "error": "Internal server error",
  "message": "Failed to connect to Notion API"
}

Rate Limiting

Currently, no rate limiting is implemented.

Future Enhancement: Rate limiting will be added to prevent abuse.


CORS

CORS is configured to allow cross-origin requests from the frontend.

Allowed Origins: Configured in config/packages/nelmio_cors.yaml

Allowed Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS

Allowed Headers: Content-Type, Authorization


Pagination

Currently, pagination is not implemented. All list endpoints return all results.

Future Enhancement: Add pagination with page and limit query parameters.

Proposed Format:

bash
GET /api/knowledge?page=1&limit=20

Proposed Response:

json
{
  "data": [...],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 150,
    "pages": 8
  }
}

Filtering and Sorting

Current Filtering

  • Knowledge: search, category
  • Categories: sync

Future Enhancements

Proposed Filtering:

bash
GET /api/knowledge?status=Done&tags=symfony,php&targetGroups=developers

Proposed Sorting:

bash
GET /api/knowledge?sort=-createdAt,title

Webhooks

Notion Webhook Endpoint

Endpoint: POST /api/notion/webhook

Receives webhook events from Notion when pages are created, updated, or deleted.

Request Body (from Notion):

json
{
  "type": "page.updated",
  "page": {
    "id": "notion-page-id",
    "last_edited_time": "2026-02-19T12:00:00.000Z"
  }
}

Response: 200 OK

json
{
  "message": "Webhook processed successfully"
}

Testing the API

Using cURL

bash
# Create knowledge item
curl -X POST http://localhost:8000/api/knowledge \
  -H "Content-Type: application/json" \
  -d '{"title": "Test", "content": "Test content"}'

# List knowledge items
curl http://localhost:8000/api/knowledge

# Get specific item
curl http://localhost:8000/api/knowledge/notion-page-id

# Update item
curl -X PUT http://localhost:8000/api/knowledge/notion-page-id \
  -H "Content-Type: application/json" \
  -d '{"status": "Done"}'

# Delete item
curl -X DELETE http://localhost:8000/api/knowledge/notion-page-id

Using HTTPie

bash
# Create knowledge item
http POST localhost:8000/api/knowledge \
  title="Test" \
  content="Test content"

# List knowledge items
http GET localhost:8000/api/knowledge

# Update item
http PUT localhost:8000/api/knowledge/notion-page-id \
  status="Done"

Using Postman

  1. Import the API endpoints into Postman
  2. Set base URL: http://localhost:8000/api
  3. Set Content-Type header: application/json
  4. Test each endpoint

API Client Examples

JavaScript (Fetch)

javascript
// Create knowledge item
async function createKnowledge(data) {
  const response = await fetch('http://localhost:8000/api/knowledge', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(data),
  });
  return response.json();
}

// List knowledge items
async function listKnowledge() {
  const response = await fetch('http://localhost:8000/api/knowledge');
  return response.json();
}

// Usage
const knowledge = await createKnowledge({
  title: 'My Knowledge',
  content: 'Content...',
  tags: ['tag1', 'tag2'],
});

JavaScript (Axios)

javascript
import axios from 'axios';

const api = axios.create({
  baseURL: 'http://localhost:8000/api',
  headers: {
    'Content-Type': 'application/json',
  },
});

// Create knowledge item
const createKnowledge = async (data) => {
  const response = await api.post('/knowledge', data);
  return response.data;
};

// List knowledge items
const listKnowledge = async () => {
  const response = await api.get('/knowledge');
  return response.data;
};

// Update knowledge item
const updateKnowledge = async (id, data) => {
  const response = await api.put(`/knowledge/${id}`, data);
  return response.data;
};

PHP (Guzzle)

php
use GuzzleHttp\Client;

$client = new Client([
    'base_uri' => 'http://localhost:8000/api',
    'headers' => [
        'Content-Type' => 'application/json',
    ],
]);

// Create knowledge item
$response = $client->post('/knowledge', [
    'json' => [
        'title' => 'My Knowledge',
        'content' => 'Content...',
        'tags' => ['tag1', 'tag2'],
    ],
]);
$knowledge = json_decode($response->getBody(), true);

// List knowledge items
$response = $client->get('/knowledge');
$items = json_decode($response->getBody(), true);

Python (Requests)

python
import requests

BASE_URL = 'http://localhost:8000/api'

# Create knowledge item
def create_knowledge(data):
    response = requests.post(
        f'{BASE_URL}/knowledge',
        json=data,
        headers={'Content-Type': 'application/json'}
    )
    return response.json()

# List knowledge items
def list_knowledge():
    response = requests.get(f'{BASE_URL}/knowledge')
    return response.json()

# Usage
knowledge = create_knowledge({
    'title': 'My Knowledge',
    'content': 'Content...',
    'tags': ['tag1', 'tag2']
})

OpenAPI/Swagger Documentation

Future Enhancement: Generate OpenAPI specification for interactive API documentation.

Proposed Tools:

  • NelmioApiDocBundle for Symfony
  • Swagger UI for interactive documentation
  • Postman collection export

Versioning

Currently, the API is unversioned.

Future Enhancement: Add API versioning:

/api/v1/knowledge
/api/v2/knowledge

Or via headers:

Accept: application/vnd.yappa.v1+json