Confluye
Endpoints

Skills

Private skill versions, listing, and progressive load.

Workspace skills under /api/v1/skills. CRUD accepts any valid bearer key. POST /api/v1/skills/load requires a workspace-scoped key. Missing token → 401 { "error": "Valid bearer API key is required" }. Load with personal/copilot → 403 { "error": "A workspace API key is required." }.

No cursor pagination. CRUD has no Idempotency-Key. Publication (request/publish/rollback) is session-only (/api/skills/{id}/publication), not v1.

Endpoints

MethodPathEnvelope
GET/api/v1/skills{ skills[] }
POST/api/v1/skills201 { skill }
GET/api/v1/skills/{id}{ skill }
PATCH/api/v1/skills/{id}{ skill }
DELETE/api/v1/skills/{id}{ deleted: true }
POST/api/v1/skills/loadProgressive load (see below)

Missing / undiscoverable id → 404 { "error": "Skill not found." }.

Skill object (CRUD)

id, workspaceId, optional ownerId, name, description, instructions, enabled, visibility (private | workspace), publicationStatus (draft | pending | published | archived), version, optional parentVersionId / reviewer / publication timestamps, usageCount, createdAt, updatedAt, optional lastUsedAt.

Discoverability: published workspace skills are visible to the workspace. Private draft/pending skills are visible only to the API key userId (the owner). Archived / unpublished rows are omitted from v1 list (includePendingReview is not exposed).

List and create

GET ?query= substring match (case-insensitive) on name, description, or instructions.

POST JSON:

FieldNotes
name + instructionsRequired unless importing content
descriptionOptional
enabledDefault true
contentSkill markdown. Frontmatter name/description and body instructions; name may come from first # heading (slugified)

Import errors → 400 ("Skill content is required.", "Skill name is required in frontmatter or first heading.", "Skill instructions are required."). Missing name/instructions → 400 { "error": "Skill name and instructions are required." }. Owner is apiKey.userId; empty owner → "A private skill owner is required.".

Creates a private draft. Same owner + case-insensitive name with an active private draft updates that draft and still returns 201. Active pending review for that name → 400 "This skill already has a version awaiting publication review.". Otherwise a new version is created (parentVersionId of the prior lineage).

Update and delete

Only the owner (canEditWorkspaceSkill). Else 400 { "error": "Only the skill owner can edit this skill." } (or delete equivalent). PATCH fields: enabled, description, instructions (not name).

Private draft updates in place. Other statuses: pending private may be archived, then a new private draft version is created and returned.

Delete: published workspace skills → 400 "Published skills cannot be deleted. Roll back or archive the publication first."

Progressive load

POST /api/v1/skills/loadCache-Control: private, no-store. Strict JSON:

FieldNotes
skillIdsUp to 32 non-empty ids (max length 191); duplicates skipped
skillIdLegacy single id
skillNameUsed only when no id selectors remain
versionsMap id → positive int
versionLegacy version for skillId / skillName

At least one of skillIds, skillId, skillName. Extra keys or invalid shape → 400 { "error": "Invalid progressive skill load request." } (no code).

Success 200:

{
  "capability": "skills.load",
  "effect": "read-only",
  "skills": [{ "id": "", "workspaceId": "", "name": "", "description": "", "instructions": "", "version": 1, "files": [] }],
  "compatibility": { "usedLegacyFields": false }
}

files[]: id, name, mimeType, sizeBytes, sha256, status: "ready".

StatusBody
400{ error, code: "PROGRESSIVE_SKILL_INVALID_REQUEST" } (service-level)
409{ error, code: "PROGRESSIVE_SKILL_VERSION_MISMATCH" }
404{ "error": "Skill not found.", "code": "PROGRESSIVE_SKILL_NOT_AVAILABLE" } (missing, unauthorized, or unread file — indistinguishable)
500{ error }

The handler enabled() is currently always true (404 "Progressive Skills is not enabled." is not reachable).

Known gaps

  • No v1 publication/review endpoints.
  • PATCH cannot rename.
  • List does not expose pending-review-for-admins (includePendingReview).
  • Load returns full instructions (CRUD list also returns them). Runtime summaries elsewhere omit instructions; this route does not.

Next steps