Skip to content

Knowledge API

Base path: /api/v1/knowledge

See API Reference for auth, errors, and pagination.

The Knowledge module manages the core institutional memory graph: entries, tags, departments, categories, version history, and entry relationships.

Enumerations

source

Value Description
manual Manually authored (default)
voice_note Captured from a voice note
slack Imported from Slack
email Imported from email
document Imported from a document
interview Captured during an interview
shift_log Captured from a shift log

status

Value Description
active Live and discoverable (default)
archived Soft-deleted / hidden
needs_review Flagged for editorial review
outdated Superseded; kept for reference

confidence

Value Description
high Highly reliable
medium Moderately reliable (default)
low Uncertain or unverified

language

Value Description
en English (default)
ar Arabic
mixed Bilingual content

visibility

Value Description
all Visible to everyone in the org (default)
role Restricted to managers and admins
specific_users Visible only to users in visible_user_ids

When visibility is specific_users, visible_user_ids must be non-empty or the request fails with INVALID_VISIBILITY_CONFIG.

Response Shapes

KnowledgeEntryResponse

Returned by create, get, update, verify, needs-review, and relate endpoints.

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "title": "How to handle a refund request",
  "content": "Full markdown content...",
  "source": "manual",
  "status": "active",
  "confidence": "high",
  "language": "en",
  "visibility": "all",
  "visible_user_ids": [],
  "location": "Dubai HQ",
  "version": 3,
  "department_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "category_id": "c1d2e3f4-a5b6-7890-abcd-ef1234567890",
  "created_by": "c0ffee00-dead-beef-cafe-000000000001",
  "verified_by": "c0ffee00-dead-beef-cafe-000000000002",
  "verified_at": "2026-05-01T10:30:00Z",
  "last_reviewed_at": "2026-05-20T08:00:00Z",
  "created_at": "2026-04-01T09:00:00Z",
  "updated_at": "2026-05-20T08:00:00Z",
  "tags": [{ "id": "tag-uuid", "name": "Refunds", "color": "#FF5733", "created_at": "...", "updated_at": "..." }],
  "department": { "id": "...", "name": "Customer Support", "description": "...", "created_at": "...", "updated_at": "..." },
  "category": { "id": "...", "org_id": "...", "name": "Policies", "description": null, "color": null, "icon": null, "parent_id": null, "sort_order": 0, "is_active": true, "created_at": "...", "updated_at": "..." },
  "creator": { "id": "...", "name": "Ali Hassan", "email": "ali@example.com" },
  "verifier": { "id": "...", "name": "Sara Ahmed", "email": "sara@example.com" },
  "related_entries": [{ "id": "some-uuid", "title": "Refund policy overview" }]
}

KnowledgeEntryListItem

Lighter shape for list views — omits content, creator, verifier, related_entries.

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "title": "How to handle a refund request",
  "source": "manual",
  "status": "active",
  "confidence": "high",
  "language": "en",
  "visibility": "all",
  "department_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "category_id": null,
  "created_by": "c0ffee00-dead-beef-cafe-000000000001",
  "verified_by": null,
  "version": 1,
  "created_at": "2026-04-01T09:00:00Z",
  "updated_at": "2026-04-01T09:00:00Z",
  "tags": [],
  "department": null,
  "category": null
}

VersionResponse

{
  "id": "ver-uuid",
  "entry_id": "entry-uuid",
  "version_number": 2,
  "title": "Old title before update",
  "content": "Old content before update...",
  "changed_by": "user-uuid",
  "changed_by_name": "Ali Hassan",
  "changed_at": "2026-05-10T14:00:00Z",
  "change_summary": "Corrected refund window from 14 to 30 days.",
  "created_at": "2026-05-10T14:00:00Z"
}

CategoryResponse

{
  "id": "cat-uuid",
  "org_id": "org-uuid",
  "name": "Policies",
  "description": "Internal policy documents",
  "color": "#3B82F6",
  "icon": "document",
  "parent_id": null,
  "sort_order": 0,
  "is_active": true,
  "created_at": "2026-01-01T00:00:00Z",
  "updated_at": "2026-01-01T00:00:00Z"
}

Knowledge Entries

GET /knowledge/

Auth: JWT · Any role · Any plan

Returns a paginated list of knowledge entries scoped to the caller's org. Members only see entries their visibility settings grant access to; managers and above see all entries.

Query parameters

Parameter Type Default Description
page integer 1 Page number (min 1)
per_page integer 20 Items per page (1–100, clamped)
department_id UUID Filter by department
tag_ids UUID[] Filter by tags — supports repeated params (?tag_ids=x&tag_ids=y) and comma-separated (?tag_ids=x,y)
status string Filter by status enum
visibility string Filter by visibility enum
language string Filter by language enum
confidence string Filter by confidence enum
search string Full-text search across title and content

Response 200 OK

{
  "items": [ /* KnowledgeEntryListItem[] */ ],
  "pagination": { "page": 1, "per_page": 20, "total": 150, "total_pages": 8, "has_next": true, "has_prev": false }
}

Errors: 400 VALIDATION_ERROR (invalid UUID params), 401, 402, 403 KNOWLEDGE_ACCESS_DENIED (members filtering by role visibility)

curl -G "https://api.knora.io/api/v1/knowledge/" \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "search=refund" \
  --data-urlencode "status=active" \
  --data-urlencode "tag_ids=tag-uuid-1,tag-uuid-2"

POST /knowledge/

Auth: JWT · Any role · Any plan

Creates a new knowledge entry. After creation, on_entry_created is dispatched asynchronously (embeddings, notifications).

Request body

Field Type Required Constraints
title string Yes 1–500 characters
content string Yes Min 1 character
source string No source enum; default manual
department_id UUID No Must exist in the org
category_id UUID No Must exist in the org
tag_ids UUID[] No Each must exist in the org; default []
visibility string No visibility enum; default all
visible_user_ids UUID[] No Required and non-empty when visibility is specific_users
status string No status enum; default active
confidence string No confidence enum; default medium
language string No language enum; default en
location string No Max 255 characters

Response 201 CreatedKnowledgeEntryResponse

Errors: 400 VALIDATION_ERROR, 400 INVALID_VISIBILITY_CONFIG, 401, 402, 404 DEPARTMENT_NOT_FOUND, 404 CATEGORY_NOT_FOUND, 404 TAG_NOT_FOUND

curl -X POST "https://api.knora.io/api/v1/knowledge/" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "How to process a refund", "content": "Open the order, click Refund, confirm amount.", "confidence": "high", "language": "en"}'

GET /knowledge/:entry_id

Auth: JWT · Any role · Any plan

Fetches a single entry with all relationships loaded. Visibility rules are enforced for members.

Response 200 OKKnowledgeEntryResponse

Errors: 401, 402, 403 KNOWLEDGE_ACCESS_DENIED, 404 KNOWLEDGE_ENTRY_NOT_FOUND

curl "https://api.knora.io/api/v1/knowledge/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer $TOKEN"

PUT /knowledge/:entry_id

Auth: JWT · Any role (creator, manager, or admin only) · Any plan

Updates one or more fields. Only provided fields are changed. Each call: - Creates an immutable KnowledgeVersion snapshot of the pre-update state. - Increments version on the entry. - Dispatches on_entry_updated asynchronously.

Request body (all fields optional)

Field Type Constraints
title string 1–500 characters
content string Min 1 character
source string source enum
department_id UUID Must exist in org; send null to unset
category_id UUID Must exist in org; send null to unset
tag_ids UUID[] Replaces all existing tags
visibility string visibility enum
visible_user_ids UUID[] Required when changing to specific_users
status string status enum
confidence string confidence enum
language string language enum
location string Max 255 characters
change_summary string Max 1000 characters; stored in the version record

Response 200 OKKnowledgeEntryResponse

Errors: 400 VALIDATION_ERROR, 400 INVALID_VISIBILITY_CONFIG, 401, 402, 403 KNOWLEDGE_ACCESS_DENIED, 404 KNOWLEDGE_ENTRY_NOT_FOUND, 404 CATEGORY_NOT_FOUND, 404 TAG_NOT_FOUND

curl -X PUT "https://api.knora.io/api/v1/knowledge/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confidence": "high", "change_summary": "Verified after Q2 policy review."}'

DELETE /knowledge/:entry_id

Auth: JWT · Any role (creator, manager, or admin only) · Any plan

Soft-deletes the entry by setting status to archived. The record and version history are preserved.

Response 200 OK

{ "message": "Entry deleted." }

Errors: 401, 402, 403 KNOWLEDGE_ACCESS_DENIED, 404 KNOWLEDGE_ENTRY_NOT_FOUND

curl -X DELETE "https://api.knora.io/api/v1/knowledge/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer $TOKEN"

POST /knowledge/:entry_id/verify

Auth: JWT · manager+ · Any plan

Marks an entry as verified. Records the verifying user and timestamp. Also restores status to active if it was outdated or needs_review. Entries already verified return 409.

Request body (optional)

Field Type Constraints
notes string Max 1000 characters; accepted but not persisted (reserved)

Response 200 OKKnowledgeEntryResponse (with verified_by and verified_at populated)

Errors: 401, 402, 403 INSUFFICIENT_ROLE, 404 KNOWLEDGE_ENTRY_NOT_FOUND, 409 KNOWLEDGE_ENTRY_ALREADY_VERIFIED

curl -X POST "https://api.knora.io/api/v1/knowledge/3fa85f64-5717-4562-b3fc-2c963f66afa6/verify" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"notes": "Reviewed in May 2026 policy sync."}'

POST /knowledge/:entry_id/needs-review

Auth: JWT · Any role · Any plan

Flags an entry with status: needs_review. Any authenticated user with visibility access can flag.

Request body (optional)

Field Type Constraints
reason string Max 1000 characters; accepted but not persisted (reserved)

Response 200 OKKnowledgeEntryResponse (with status: "needs_review")

Errors: 401, 402, 403 KNOWLEDGE_ACCESS_DENIED, 404 KNOWLEDGE_ENTRY_NOT_FOUND

curl -X POST "https://api.knora.io/api/v1/knowledge/3fa85f64-5717-4562-b3fc-2c963f66afa6/needs-review" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Policy changed."}'

GET /knowledge/:entry_id/versions

Auth: JWT · Any role (visibility-gated) · Any plan

Returns the full version history ordered newest-first. Each version is an immutable snapshot of title and content captured before the update was applied.

Response 200 OK — Array of VersionResponse

Errors: 401, 402, 403 KNOWLEDGE_ACCESS_DENIED, 404 KNOWLEDGE_ENTRY_NOT_FOUND

curl "https://api.knora.io/api/v1/knowledge/3fa85f64-5717-4562-b3fc-2c963f66afa6/versions" \
  -H "Authorization: Bearer $TOKEN"

POST /knowledge/:entry_id/relate

Auth: JWT · Any role · Any plan

Links two entries. Both must belong to the same org. Self-relation is rejected.

Request body

Field Type Required Description
related_entry_id UUID Yes Target entry to link

Response 200 OKKnowledgeEntryResponse (source entry with updated related_entries)

Errors: 400 VALIDATION_ERROR, 400 SELF_RELATION_NOT_ALLOWED, 401, 402, 403 KNOWLEDGE_ACCESS_DENIED, 404 KNOWLEDGE_ENTRY_NOT_FOUND, 404 RELATED_ENTRY_NOT_FOUND

curl -X POST "https://api.knora.io/api/v1/knowledge/3fa85f64-5717-4562-b3fc-2c963f66afa6/relate" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"related_entry_id": "other-entry-uuid"}'

DELETE /knowledge/:entry_id/relate/:related_id

Auth: JWT · Any role · Any plan

Removes the link between two entries.

Response 200 OKKnowledgeEntryResponse (source entry with updated related_entries)

Errors: 401, 402, 403 KNOWLEDGE_ACCESS_DENIED, 404 KNOWLEDGE_ENTRY_NOT_FOUND, 404 RELATED_ENTRY_NOT_FOUND

curl -X DELETE \
  "https://api.knora.io/api/v1/knowledge/3fa85f64-5717-4562-b3fc-2c963f66afa6/relate/other-entry-uuid" \
  -H "Authorization: Bearer $TOKEN"

Bulk Operations

Both bulk endpoints require the Growth plan (feature flag: knowledge_graph).

POST /knowledge/bulk-import

Auth: JWT · Any role · Growth+ (knowledge_graph feature)

Imports an array of entries. Departments and tags are resolved by name — they are created automatically if they do not exist. Per-item failures are skipped and reported in errors; the rest are saved.

Request body

{
  "entries": [
    {
      "title": "Opening procedure for Dubai store",
      "content": "1. Unlock the shutters...",
      "source": "document",
      "department_name": "Operations",
      "tag_names": ["Procedures", "Dubai"],
      "visibility": "all",
      "status": "active",
      "confidence": "high",
      "language": "en",
      "location": "Dubai Mall"
    }
  ]
}

BulkImportItem fields

Field Type Required Constraints
title string Yes 1–500 characters
content string Yes Min 1 character
source string No source enum; default manual
department_name string No Resolved or created by name
tag_names string[] No Each resolved or created by name
visibility string No visibility enum; default all
status string No status enum; default active
confidence string No confidence enum; default medium
language string No language enum; default en
location string No Free-form location string

The entries array must contain at least one item.

Response 200 OK

{
  "created": 47,
  "failed": 2,
  "errors": [
    { "index": 3, "title": "Some bad entry", "message": "title: String should have at most 500 characters" }
  ]
}

Errors: 400 VALIDATION_ERROR (malformed envelope), 401, 402, 403 PLAN_UPGRADE_REQUIRED

curl -X POST "https://api.knora.io/api/v1/knowledge/bulk-import" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"entries": [{"title": "Opening procedure", "content": "Unlock shutters, disable alarm.", "department_name": "Operations", "tag_names": ["Procedures"], "confidence": "high"}]}'

GET /knowledge/export

Auth: JWT · Any role · Growth+ (knowledge_graph feature)

Exports all matching entries as a flat JSON array. Same visibility rules as list. Hard-capped at 5000 entries; use the paginated list endpoint for larger datasets.

Query parameters

Parameter Type Description
department_id UUID Filter by department
tag_ids UUID[] Filter by tags — supports repeated params and comma-separated
status string Filter by status enum
language string Filter by language enum

Response 200 OK

{
  "entries": [ /* KnowledgeEntryResponse[] — full shape including content */ ],
  "total": 47
}

Errors: 400 VALIDATION_ERROR (invalid UUID params), 401, 402, 403 PLAN_UPGRADE_REQUIRED

curl -G "https://api.knora.io/api/v1/knowledge/export" \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "status=active" \
  --data-urlencode "language=en"

Tags

Tags are flat, org-scoped labels applied to entries for categorization and filtering.

GET /knowledge/tags

Auth: JWT · Any role · Any plan

Returns all tags for the caller's org.

Response 200 OK — Array of tag objects

[{ "id": "tag-uuid", "name": "Refunds", "color": "#FF5733", "created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z" }]

Errors: 401, 402

curl "https://api.knora.io/api/v1/knowledge/tags" -H "Authorization: Bearer $TOKEN"

POST /knowledge/tags

Auth: JWT · manager+ · Any plan

Creates a tag. Names are unique within an org (case-sensitive).

Request body

Field Type Required Constraints
name string Yes 1–100 characters; unique within org
color string No Hex color #RRGGBB

Response 201 Created — Tag object

Errors: 400 VALIDATION_ERROR, 401, 402, 403 INSUFFICIENT_ROLE, 409 TAG_ALREADY_EXISTS

curl -X POST "https://api.knora.io/api/v1/knowledge/tags" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Refunds", "color": "#FF5733"}'

DELETE /knowledge/tags/:tag_id

Auth: JWT · manager+ · Any plan

Deletes a tag and removes it from all entries (cascade via join table).

Response 200 OK

{ "message": "Tag deleted successfully." }

Errors: 401, 402, 403 INSUFFICIENT_ROLE, 404 TAG_NOT_FOUND

curl -X DELETE "https://api.knora.io/api/v1/knowledge/tags/tag-uuid" \
  -H "Authorization: Bearer $TOKEN"

Departments

Departments are org-scoped organizational units for grouping and filtering entries.

GET /knowledge/departments

Auth: JWT · Any role · Any plan

Returns all departments for the caller's org.

Response 200 OK — Array of department objects

[{ "id": "dept-uuid", "name": "Customer Support", "description": "Handles customer queries", "created_at": "...", "updated_at": "..." }]

Errors: 401, 402

curl "https://api.knora.io/api/v1/knowledge/departments" -H "Authorization: Bearer $TOKEN"

POST /knowledge/departments

Auth: JWT · manager+ · Any plan

Creates a department. Names are unique within an org.

Request body

Field Type Required Constraints
name string Yes 1–255 characters; unique within org
description string No Max 2000 characters

Response 201 Created — Department object

Errors: 400 VALIDATION_ERROR, 401, 402, 403 INSUFFICIENT_ROLE, 409 DEPARTMENT_ALREADY_EXISTS

curl -X POST "https://api.knora.io/api/v1/knowledge/departments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Customer Support", "description": "Front-line support team."}'

PUT /knowledge/departments/:dept_id

Auth: JWT · manager+ · Any plan

Updates a department's name and/or description. Only provided fields are changed.

Request body (all optional)

Field Type Constraints
name string 1–255 characters; unique within org
description string Max 2000 characters

Response 200 OK — Department object

Errors: 400 VALIDATION_ERROR, 401, 402, 403 INSUFFICIENT_ROLE, 404 DEPARTMENT_NOT_FOUND, 409 DEPARTMENT_ALREADY_EXISTS

curl -X PUT "https://api.knora.io/api/v1/knowledge/departments/dept-uuid" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Customer Success"}'

POST /knowledge/departments/classify

Auth: JWT · manager+ · Any plan

Auto-classifies knowledge entries into departments using AI. When dry_run=true the classification runs synchronously and returns a preview without making any changes. Without dry_run, the task is enqueued and returns 202 Accepted.

Query parameters

Parameter Type Default Description
dry_run boolean false Run synchronously and preview results without persisting

Response 200 OK (dry run) — Classification preview object returned by the task

Response 202 Accepted (live run)

{ "message": "Department classification started", "dry_run": false }

Errors: 401, 402, 403 INSUFFICIENT_ROLE

# Preview without changes
curl -X POST "https://api.knora.io/api/v1/knowledge/departments/classify?dry_run=true" \
  -H "Authorization: Bearer $TOKEN"

# Enqueue classification
curl -X POST "https://api.knora.io/api/v1/knowledge/departments/classify" \
  -H "Authorization: Bearer $TOKEN"

Categories

Categories provide a hierarchical (tree) taxonomy for entries. All category endpoints require the Growth plan and the custom_categories feature. Categories support nested structures via parent_id.

GET /knowledge/categories

Auth: JWT · Any role · Growth+ (custom_categories feature)

Lists org categories.

Query parameters

Parameter Type Default Description
format string flat flat — flat array; tree — root nodes with recursive children
active boolean false When true, returns only active categories

Response 200 OK

  • format=flat — Array of CategoryResponse
  • format=tree — Array of CategoryTree (each node has children: CategoryTree[])

Errors: 401, 402, 403 PLAN_UPGRADE_REQUIRED

curl "https://api.knora.io/api/v1/knowledge/categories?format=tree&active=true" \
  -H "Authorization: Bearer $TOKEN"

POST /knowledge/categories

Auth: JWT · manager+ · Growth+ (custom_categories feature)

Creates a custom category. Names are unique within the same parent scope.

Request body

Field Type Required Constraints
name string Yes 1–255 characters; unique under the same parent
description string No Max 2000 characters
color string No Hex color #RRGGBB
icon string No Max 100 characters
parent_id UUID No Parent category; must belong to the same org
sort_order integer No Non-negative integer; default 0

Response 201 CreatedCategoryResponse

Errors: 400 VALIDATION_ERROR, 401, 402, 403 INSUFFICIENT_ROLE, 403 PLAN_UPGRADE_REQUIRED, 404 CATEGORY_NOT_FOUND (invalid parent), 409 CATEGORY_ALREADY_EXISTS

curl -X POST "https://api.knora.io/api/v1/knowledge/categories" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Policies", "color": "#3B82F6", "sort_order": 1}'

PUT /knowledge/categories/:id

Auth: JWT · manager+ · Growth+ (custom_categories feature)

Updates a category. Reparenting is supported via parent_id; circular references are rejected. Only provided fields are changed.

Request body (all optional)

Field Type Constraints
name string 1–255 characters
description string Max 2000 characters
color string Hex color #RRGGBB
icon string Max 100 characters
parent_id UUID New parent; must belong to same org; cannot be a descendant of this category
sort_order integer Non-negative integer
is_active boolean Activate or deactivate

Response 200 OKCategoryResponse

Errors: 400 VALIDATION_ERROR, 400 CATEGORY_CIRCULAR_REFERENCE, 401, 402, 403 INSUFFICIENT_ROLE, 403 PLAN_UPGRADE_REQUIRED, 404 CATEGORY_NOT_FOUND, 409 CATEGORY_ALREADY_EXISTS

curl -X PUT "https://api.knora.io/api/v1/knowledge/categories/cat-uuid" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "HR Policies", "color": "#10B981"}'

DELETE /knowledge/categories/:id

Auth: JWT · manager+ · Growth+ (custom_categories feature)

Soft-deactivates a category and all its descendants. Records are preserved.

Response 200 OK

{ "message": "Category deactivated." }

Errors: 401, 402, 403 INSUFFICIENT_ROLE, 403 PLAN_UPGRADE_REQUIRED, 404 CATEGORY_NOT_FOUND

curl -X DELETE "https://api.knora.io/api/v1/knowledge/categories/cat-uuid" \
  -H "Authorization: Bearer $TOKEN"

GET /knowledge/categories/:id/path

Auth: JWT · Any role · Growth+ (custom_categories feature)

Returns the full ancestor path from the root category down to (and including) the requested category.

Response 200 OK — Ordered array of CategoryResponse (root first)

[
  { "id": "root-uuid", "name": "Company Knowledge", "parent_id": null, ... },
  { "id": "mid-uuid", "name": "HR", "parent_id": "root-uuid", ... },
  { "id": "cat-uuid", "name": "HR Policies", "parent_id": "mid-uuid", ... }
]

Errors: 401, 402, 403 PLAN_UPGRADE_REQUIRED, 404 CATEGORY_NOT_FOUND

curl "https://api.knora.io/api/v1/knowledge/categories/cat-uuid/path" \
  -H "Authorization: Bearer $TOKEN"

Notes

Versioning — Every PUT /knowledge/:id call creates an immutable version snapshot of the previous state. The optional change_summary field is stored with the record for a human-readable audit trail. Version numbers increment from 1. Retrieve history via GET /knowledge/:id/versions.

Visibility and access — Members only see entries where visibility is all or specific_users with their ID included. role visibility is restricted to managers and above.

Background taskson_entry_created and on_entry_updated are Celery tasks dispatched asynchronously. HTTP responses return immediately without waiting for task completion.

Multi-tenancy — All resources (entries, tags, departments, categories, versions) are strictly scoped to the caller's org via the org_id JWT claim. Cross-org access is impossible through these endpoints.

Tag filtering formattag_ids supports both repeated params (?tag_ids=x&tag_ids=y) and comma-separated values (?tag_ids=x,y). Both formats can be mixed in the same request.