After Wrangler (or you) finalizes a Skill Collection in the dashboard, your coding agent loads skills from files on disk. Use client.skills.pull() to fetch the collection, then write the files to your project.
You can generate these exact snippets for your own collection from the dashboard: open a Skill Collection and click Pull via SDK.
Prerequisites
Set your API key in the environment:
export PROMPTLAYER_API_KEY="your_promptlayer_api_key"
Pull as a folder of files
skills.pull() returns a response with a files map (path → text content). For provider-specific collections, those paths already include the provider prefix (.claude/..., .agents/..., or .openclaw/...), so the simplest thing to do is write each file at its returned path from your project root. Everything lands where your agent expects it.
# pip install -U promptlayer
import os
from pathlib import Path
from promptlayer import PromptLayer
client = PromptLayer(api_key=os.environ["PROMPTLAYER_API_KEY"])
result = client.skills.pull("my-collection")
if result is None:
raise ValueError("No skill collection returned.")
for relative_path, content in result["files"].items():
output_path = Path(relative_path)
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(content, encoding="utf-8")
For a Claude Code collection, this produces .claude/CLAUDE.md, .claude/skills/<name>/SKILL.md, etc. For Codex, you get .agents/AGENTS.md, and for OpenClaw you get .openclaw/.... Universal collections have no prefix and land in your project root as-is.
Writing to a custom directory
If you want to rename the output directory (for example, write a Claude Code collection under .agents instead of .claude), strip the known provider prefix from each path and join the remainder to your target directory:
output_dir = Path("custom-dir")
prefix = ".claude/" # your collection's provider prefix
for relative_path, content in result["files"].items():
target = relative_path[len(prefix):] if relative_path.startswith(prefix) else relative_path
output_path = output_dir / target
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(content, encoding="utf-8")
| Provider | Prefix in response paths |
|---|
| Claude Code | .claude/ |
| OpenAI / Codex | .agents/ |
| OpenClaw | .openclaw/ |
| Universal | (no prefix) |
Pin a version or release label
Pass version= or label= to pull an immutable snapshot instead of latest.
client.skills.pull("my-collection", version=12)
client.skills.pull("my-collection", label="production")
Pull as a zip archive
Pass format="zip" to get a binary archive instead of a JSON file map. The archive already contains the provider’s on-disk layout, so unzip it at your project root.
import os
from pathlib import Path
from promptlayer import PromptLayer
client = PromptLayer(api_key=os.environ["PROMPTLAYER_API_KEY"])
zip_archive = client.skills.pull("my-collection", format="zip")
if not isinstance(zip_archive, bytes):
raise ValueError("Expected a zip archive.")
Path("skills.zip").write_bytes(zip_archive)
Raw REST API
Use GET against the public API when you are not using an SDK:
GET https://api.promptlayer.com/api/public/v2/skill-collections/<identifier>
Optional query parameters:
| Parameter | Purpose |
|---|
format=zip | Return a zip archive instead of JSON |
version=<n> | Pin to a specific version number |
label=<name> | Pin to a release label |
File paths in the JSON response follow the same convention as the SDK: provider-specific collections include the .claude/, .agents/, or .openclaw/ prefix; universal collections are root-relative. Zip exports always include the provider layout.
Skill Collection webhooks
Skill Collections emit a skill_collection_files_changed event when a collection is created, saved, deleted, or restored. To subscribe and verify signatures, see Webhooks.
CI pattern
Run skills.pull in your build or deploy pipeline with a pinned label (for example production) so your agent loads a stable snapshot instead of whatever was latest when the job last ran.
For more on labels and review, see Tune skills for your team.