---
title: "Add a published catalog skill to the caller's library by reference"
method: POST
path: "/api/v1/library/refs"
tags: ["Library"]
---

# Add a published catalog skill to the caller's library by reference

`POST /api/v1/library/refs`

## Request

- Base URL: `https://skillrepo.dev`
- URL: `https://skillrepo.dev/api/v1/library/refs`
- Auth: HTTP bearer

Adds an existing public skill (resolved by `owner/name`) to the authenticated account's library. The route is the canonical home for catalog-reference adds; the older `POST /api/v1/library` shape will be rebound to multipart file-push in #1452.

Supports the optional `Idempotency-Key` request header. Retries with the same key + same request body return the cached response with `Idempotent-Replayed: true`. Retries with the same key + a different body return 422 `idempotency_key_reused`. Concurrent retries while the first is in-flight return 409 `idempotency_key_in_progress`. Cached responses live for 24h.

Error codes are carried in the `code` field of the error envelope so clients can distinguish between `not_found`, `self_ownership`, `already_in_library`, `plan_limit`, `insufficient_scope`, `validation_error`, `idempotency_key_in_progress`, and `idempotency_key_reused`.

## Headers

- `Idempotency-Key` string: Client-supplied idempotency key (max 255 chars). Retries with the same key + same request body return the cached response (with `Idempotent-Replayed: true`) instead of re-executing the handler. Retries with the same key + a different body return 422 `idempotency_key_reused`. Concurrent retries while the original is in-flight return 409 `idempotency_key_in_progress`. Cached responses live for 24 hours.

## Request body

- AddLibraryBody: Request body for adding an existing public skill to the caller's library.
  - `owner` string, required
  - `name` string, required

## Response `201`

Skill added to the library. On Idempotency-Key replays the same 201 is returned verbatim with the `Idempotent-Replayed: true` header set — the middleware preserves the original response status, so first-call and replay share a single status code, distinguished only by the response header.

- AddLibrarySuccessResponse: Response returned after successfully adding a skill to the library.
  - `added` object, required
    - `owner` string, required
    - `name` string, required
    - `version` string, nullable, required: Current version of the added skill (null if the skill has no published version yet).
    - `addedAt` string, date-time, required

## Other responses

- `400`: Invalid request — malformed JSON, missing required fields, or failed schema validation. Body matches `ErrorEnvelope`.
- `401`: Missing or invalid authentication. For routes that require `bearerAccessKey` or `bearerCatalogKey`.
- `403`: Authenticated, but the key lacks the required scope or the account is suspended. Use `code` to distinguish: `insufficient_scope`, `account_suspended`, `plan_limit`.
- `404`: Resource does not exist, OR exists but the caller has no permission to see it. Private resources return 404 (never 403) to avoid leaking existence.
- `409`: Conflict — the skill is owned by the caller (`self_ownership`), is already in the caller's library (`already_in_library`), or an in-flight request with the same Idempotency-Key is still being processed (`idempotency_key_in_progress`).
- `422`: Idempotency-Key was reused with a different request body (`idempotency_key_reused`).
- `429`: Rate limit exceeded on the route's bucket. Includes `X-RateLimit-*` headers indicating the limit, remaining, and reset time.

---

[API](https://skmtc.dev/skillrepo/apis/skillrepo-api.md) · [All operations](https://skmtc.dev/skillrepo/apis/skillrepo-api/llms.txt) · [OpenAPI document](https://skmtc.dev/skillrepo/apis/skillrepo-api/revisions/b62bac246918?raw)
