Skip to main content

Endpoint

skills.create
This endpoint requires authentication and validates write permissions for the target vault.

Input Schema

slug
string
required
URL-safe identifier for the skill. Must be unique within the target vault.
name
string
required
Display name of the skill (minimum 1 character)
description
string
required
Brief description of the skill (minimum 1 character)
skillMarkdown
string
default:"''"
Markdown content with optional mention syntax
frontmatter
object
default:"{}"
YAML frontmatter as a key-value object
metadata
object
default:"{}"
Additional metadata key-value pairs
sourceUrl
string | null
Original source URL if importing from external source
sourceIdentifier
string | null
Original source identifier (e.g., GitHub path, package name)
resources
array
default:"[]"
Array of bundled resource files to create with the skill
vaultId
string
UUID of the target vault. If omitted, defaults to the user’s personal vault.

Output Schema

Returns the complete created skill object (same format as skills.getById). See the Get Skill documentation for the full response schema.

Behavior

Vault Resolution

  1. If vaultId is provided → validates user has write permission to that vault
  2. If vaultId is omitted → defaults to user’s personal vault
  3. Throws FORBIDDEN if user lacks write permission

Mention Validation

Before creating the skill, all mention syntax is validated:
  • Mentions must reference existing skills or resources
  • Mentioned targets must be in vaults accessible to the target vault (same-vault or cross-vault with read permission)
  • Invalid mentions throw BAD_REQUEST with details
Mention syntax:
  • Skill mention: @[skill-name](skill-id)
  • Resource mention: @[resource-path](resource-id)
After creating the skill and resources:
  1. Parses all mention syntax in skillMarkdown and resource content
  2. Creates skillLink records in the database for each mention
  3. Links track source → target relationships for graph traversal
  4. If link sync fails, the entire skill creation is rolled back

Slug Uniqueness

Slugs must be unique within the target vault. Duplicate slug throws a database constraint error.

TypeScript Example

import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '@better-skills/api';

const client = createTRPCClient<AppRouter>({
  links: [
    httpBatchLink({
      url: 'http://localhost:3000/trpc',
      headers: {
        authorization: `Bearer ${token}`,
      },
    }),
  ],
});

// Create a skill in personal vault (default)
const skill = await client.skills.create.mutate({
  slug: 'docker-best-practices',
  name: 'Docker Best Practices',
  description: 'Production-ready Docker configuration patterns',
  skillMarkdown: `
# Docker Best Practices

Refer to @[docker-compose](${composeSkillId}) for orchestration.

## Multi-stage Builds

Use multi-stage builds to minimize image size...
  `.trim(),
  frontmatter: {
    tags: ['docker', 'devops', 'containers'],
    difficulty: 'intermediate',
  },
  metadata: {
    lastReviewed: new Date().toISOString(),
  },
  resources: [
    {
      path: 'Dockerfile.example',
      kind: 'script',
      content: `FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
CMD ["node", "server.js"]`,
      metadata: {
        language: 'dockerfile',
      },
    },
    {
      path: 'references/official-docs.md',
      kind: 'reference',
      content: '# Official Documentation\n\nhttps://docs.docker.com/develop/dev-best-practices/',
      metadata: {
        url: 'https://docs.docker.com/develop/dev-best-practices/',
      },
    },
  ],
});

console.log(`Created skill: ${skill.id}`);
console.log(`Resources: ${skill.resources.length}`);
console.log(`Vault: ${skill.vault.name}`);

Create in Specific Vault

// Create in an enterprise vault
const enterpriseSkill = await client.skills.create.mutate({
  slug: 'team-coding-standards',
  name: 'Team Coding Standards',
  description: 'Shared coding conventions for our team',
  skillMarkdown: '# Coding Standards\n\n...',
  vaultId: 'enterprise-vault-uuid',
});

With External Source Tracking

// Import a skill from GitHub
const importedSkill = await client.skills.create.mutate({
  slug: 'github-actions-ci',
  name: 'GitHub Actions CI/CD',
  description: 'CI/CD pipeline using GitHub Actions',
  skillMarkdown: fetchedMarkdown,
  sourceUrl: 'https://github.com/example/skills/actions-ci',
  sourceIdentifier: 'example/skills/actions-ci@main',
  resources: [
    {
      path: '.github/workflows/ci.yml',
      kind: 'script',
      content: ciYamlContent,
    },
  ],
});

Error Handling

BAD_REQUEST
  • Invalid mention syntax
  • Mention references non-existent skill/resource
  • Mention references inaccessible vault
  • Invalid input schema (missing required fields, invalid types)
FORBIDDEN
  • User lacks write permission to target vault
  • Vault is read-only (system_default or enterprise without admin role)
INTERNAL_SERVER_ERROR
  • Personal vault not found (should never happen)
  • Database constraint violation (e.g., duplicate slug)
  • Failed to create skill or resources
UNAUTHORIZED
No valid session

Notes

  • All mention syntax is validated before creating the skill
  • Resource creation is atomic with skill creation
  • Link synchronization happens automatically after successful creation
  • If any step fails, the entire transaction is rolled back
  • The returned skill includes rendered markdown with mentions processed
  • Personal vaults are always writable by the owner
  • System default vaults are always read-only (cannot create skills)

Build docs developers (and LLMs) love