API Reference
Complete API reference for the Yappa Knowledge Hub backend.
Base URL
http://localhost:8000/apiIn production, replace with your actual domain.
Content Type
All requests and responses use JSON:
Content-Type: application/jsonAuthentication
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:
{
"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 titlecontent(string, optional) - Full content/descriptiontags(array, optional) - Array of tag stringscategory/categoryId(string, optional) - Notion category page IDtargetGroups(array, optional) - Target groups (inherits from category if not provided)status(string, optional) - Status (default: "Not started")url(string, optional) - Source URLuserId(string, optional) - User ID (Slack user ID)sourceMessage(object, optional) - Original Slack message metadata
Response: 201 Created
{
"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:
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 querycategory(string, optional) - Filter by category ID
Examples:
# List all
GET /api/knowledge
# Search
GET /api/knowledge?search=symfony
# Filter by category
GET /api/knowledge?category=notion-category-idResponse: 200 OK
[
{
"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
{
"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
{
"error": "Knowledge item not found"
}Example:
curl http://localhost:8000/api/knowledge/a1b2c3d4-e5f6-7890-abcd-ef1234567890Update 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):
{
"title": "Updated Title",
"content": "Updated content...",
"tags": ["new", "tags"],
"targetGroups": ["updated-groups"],
"status": "Done",
"category": "new-category-id"
}Response: 200 OK
{
"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:
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
{
"message": "Knowledge archived successfully"
}Example:
curl -X DELETE http://localhost:8000/api/knowledge/a1b2c3d4-e5f6-7890-abcd-ef1234567890Search Knowledge Items
Alternative search endpoint using POST.
Endpoint: POST /api/knowledge/search
Request Body:
{
"query": "symfony deployment",
"q": "symfony deployment"
}Response: 200 OK
[
{
"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
[
{
"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
[
{
"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:
{
"name": "New Category",
"description": "Category description",
"icon": ":rocket:",
"targetGroups": ["developers", "designers"],
"defaultTargetGroups": ["developers", "designers"]
}Request Fields:
name(string, required) - Category namedescription(string, optional) - Category descriptionicon(string, optional) - Emoji icon (Slack format:emoji:or Unicode)targetGroups/defaultTargetGroups(array, optional) - Default target groups
Response: 201 Created
{
"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:
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:
# With auto-sync (default)
GET /api/categories
# Without auto-sync (faster)
GET /api/categories?sync=falseResponse: 200 OK
[
{
"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
{
"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):
{
"name": "Updated Name",
"description": "Updated description",
"icon": "",
"targetGroups": ["developers", "designers", "product"]
}Response: 200 OK
{
"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
{
"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
{
"message": "Categories synced successfully",
"count": 15
}Error Response: 500 Internal Server Error
{
"error": "Sync failed",
"message": "Notion API error: ..."
}Example:
curl -X POST http://localhost:8000/api/categories/syncError Responses
Standard Error Format
All errors follow this format:
{
"error": "Error type",
"message": "Detailed error message"
}HTTP Status Codes
200 OK- Successful GET, PUT, PATCH, DELETE201 Created- Successful POST (resource created)204 No Content- Successful DELETE with no response body400 Bad Request- Invalid request data404 Not Found- Resource not found500 Internal Server Error- Server error
Common Errors
400 Bad Request
{
"error": "Invalid request",
"message": "Missing required field: title"
}404 Not Found
{
"error": "Not found",
"message": "Knowledge item with ID 'xyz' not found"
}500 Internal Server Error
{
"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:
GET /api/knowledge?page=1&limit=20Proposed Response:
{
"data": [...],
"meta": {
"page": 1,
"limit": 20,
"total": 150,
"pages": 8
}
}Filtering and Sorting
Current Filtering
- Knowledge:
search,category - Categories:
sync
Future Enhancements
Proposed Filtering:
GET /api/knowledge?status=Done&tags=symfony,php&targetGroups=developersProposed Sorting:
GET /api/knowledge?sort=-createdAt,titleWebhooks
Notion Webhook Endpoint
Endpoint: POST /api/notion/webhook
Receives webhook events from Notion when pages are created, updated, or deleted.
Request Body (from Notion):
{
"type": "page.updated",
"page": {
"id": "notion-page-id",
"last_edited_time": "2026-02-19T12:00:00.000Z"
}
}Response: 200 OK
{
"message": "Webhook processed successfully"
}Testing the API
Using cURL
# 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-idUsing HTTPie
# 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
- Import the API endpoints into Postman
- Set base URL:
http://localhost:8000/api - Set Content-Type header:
application/json - Test each endpoint
API Client Examples
JavaScript (Fetch)
// 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)
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)
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)
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/knowledgeOr via headers:
Accept: application/vnd.yappa.v1+json