Skip to main content

Endpoints

skills.getById
skills.getBySlug
Both endpoints require authentication and validate vault read permissions.

skills.getById

Retrieve a skill by its unique UUID.

Input Schema

id
string
required
UUID of the skill to retrieve
Whether to convert mention syntax to HTML links in rendered content

skills.getBySlug

Retrieve a skill by its slug identifier. Slugs are unique within a vault.

Input Schema

slug
string
required
Slug identifier of the skill (case-insensitive)
vaultSlug
string
Vault slug to disambiguate when the same skill slug exists in multiple vaults
Whether to convert mention syntax to HTML links in rendered content

Slug Resolution

When vaultSlug is omitted:
  • If the slug exists in only one accessible vault → returns that skill
  • If the slug exists in multiple vaults → throws CONFLICT error with vault disambiguation list
To resolve conflicts, provide the vaultSlug or use the format <vault-slug>/<skill-slug>.

Output Schema

Both endpoints return the same complete skill object:
id
string
UUID of the skill
ownerUserId
string | null
User ID of the skill owner (null for system skills)
slug
string
URL-safe identifier for the skill
name
string
Display name of the skill
description
string
Brief description of the skill
originalMarkdown
string
Raw markdown content as stored in the database, with original mention syntax
renderedMarkdown
string
Processed markdown with mentions resolved. If linkMentions: true, mentions are converted to HTML links.
frontmatter
object
Parsed YAML frontmatter from the skill markdown
metadata
object
Additional metadata key-value pairs
isDefault
boolean
Whether this is a default system skill
sourceUrl
string | null
Original source URL if imported
sourceIdentifier
string | null
Original source identifier if imported
resources
array
Array of bundled resource files (scripts, references, assets)
vault
object
Vault metadata with user-specific permissions
createdAt
Date
Timestamp when the skill was created
updatedAt
Date
Timestamp when the skill was last updated

Markdown Rendering

originalMarkdown vs renderedMarkdown

  • originalMarkdown: Contains raw mention syntax like @[skill-name](skill-id) or @[resource-path](resource-id)
  • renderedMarkdown: Mentions are processed:
    • If linkMentions: false → mentions are replaced with plain text labels
    • If linkMentions: true → mentions are converted to clickable HTML links
The same processing applies to content (original) and renderedContent (processed) in resources.

TypeScript Examples

Fetch by UUID

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}`,
      },
    }),
  ],
});

const skill = await client.skills.getById.query({
  id: '123e4567-e89b-12d3-a456-426614174000',
  linkMentions: true,
});

console.log(skill.name);
console.log(skill.renderedMarkdown); // with HTML links
console.log(`Resources: ${skill.resources.length}`);
console.log(`Vault: ${skill.vault.name} (${skill.vault.type})`);

Fetch by Slug

// Simple case: slug exists in only one vault
const dockerSkill = await client.skills.getBySlug.query({
  slug: 'docker-compose',
});

// Disambiguate when slug exists in multiple vaults
try {
  const skill = await client.skills.getBySlug.query({
    slug: 'next-best-practices',
  });
} catch (error) {
  // CONFLICT error lists available vaults:
  // "ambiguous skill slug 'next-best-practices'; provide <vault-slug>/<skill-slug>
  // matches:
  // - personal/next-best-practices (My Vault)
  // - team-alpha/next-best-practices (Team Alpha)"
}

// Resolve by specifying vault
const skill = await client.skills.getBySlug.query({
  slug: 'next-best-practices',
  vaultSlug: 'team-alpha',
});

Access Resources

const skill = await client.skills.getById.query({
  id: skillId,
  linkMentions: false,
});

// Find a specific resource
const setupScript = skill.resources.find(
  r => r.kind === 'script' && r.path === 'setup.sh'
);

if (setupScript) {
  console.log('Setup script content:');
  console.log(setupScript.content);
}

// List all references
const references = skill.resources.filter(r => r.kind === 'reference');
references.forEach(ref => {
  console.log(`${ref.path}: ${ref.metadata.title || 'Untitled'}`);
});

Error Handling

  • NOT_FOUND: Skill does not exist or user lacks read permission
  • CONFLICT: Slug exists in multiple vaults (only for getBySlug)
  • UNAUTHORIZED: No valid session

Notes

  • Both endpoints validate vault read permissions before returning data
  • Skills in disabled vaults (isEnabled: false) are still accessible but marked accordingly
  • System default skills (type: "system_default") are always read-only
  • Enterprise vault read-only status depends on user role (admin vs. member)

Build docs developers (and LLMs) love