---
summary: "Register a skill, publish a bundle, bind it to a scope, and resolve it \u2014\
  \ five minutes end-to-end."
title: Quickstart
path: quickstart
status: published
---

# Quickstart

In five minutes you'll have a published skill bound to a workspace and resolvable from the runtime. We'll use a trivial "summarise" skill so you can focus on the surface, not the content.

You need:

- A ScaiGrid API key with `scaiskills:publish` and `scaiskills:bind` (any tenant admin has both).
- `tar` and `curl` (or the language equivalents below).

```bash
export SCAIGRID_HOST="https://scaigrid.scailabs.ai"
export SCAIGRID_API_KEY="sgk_..."
export WORKSPACE_ID="ten_..."   # your tenant / workspace id
```

## 1. Register the skill identity

Register the slug first — it owns all future versions.

```bash
curl -X POST "$SCAIGRID_HOST/v1/modules/scaiskills/skills" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "summarise",
    "visibility": "private",
    "description": "Summarise a passage in three sentences."
  }'
```

```python
import httpx, os
res = httpx.post(
    f"{os.environ['SCAIGRID_HOST']}/v1/modules/scaiskills/skills",
    headers={"Authorization": f"Bearer {os.environ['SCAIGRID_API_KEY']}"},
    json={
        "slug": "summarise",
        "visibility": "private",
        "description": "Summarise a passage in three sentences.",
    },
).json()
print(res["data"]["id"])
```

```javascript
const res = await fetch(`${process.env.SCAIGRID_HOST}/v1/modules/scaiskills/skills`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SCAIGRID_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    slug: "summarise",
    visibility: "private",
    description: "Summarise a passage in three sentences.",
  }),
});
console.log((await res.json()).data.id);
```

Save the returned `skill.id` and the slug `summarise`.

## 2. Build a bundle

A bundle is a `.tar.gz` with a `SKILL.md` at the root. The YAML frontmatter is the manifest; everything below the closing `---` is the body the LLM eventually reads.

```bash
mkdir -p summarise/references
cat > summarise/SKILL.md <<'EOF'
---
name: summarise
version: 0.1.0
description: Summarise a passage in three sentences.
triggers:
  - summarise
  - tl;dr
permissions: []
secrets: []
EOF
echo "When the user asks for a summary, produce exactly three sentences." >> summarise/SKILL.md

tar -czf summarise-0.1.0.tar.gz -C summarise SKILL.md references
```

## 3. Publish the version

```bash
curl -X POST "$SCAIGRID_HOST/v1/modules/scaiskills/skills/summarise/versions" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -F "bundle=@summarise-0.1.0.tar.gz"
```

The response carries the `content_hash` (SHA-256), the `storage_uri`, and `status: "published"`. Re-uploading the same bytes will dedup on hash.

## 4. Bind the skill to a scope

Pin to the exact version for now. `@latest` and `^0.1` are also valid in the `version` field. Use the `skill.id` you saved in step 1.

```bash
curl -X POST "$SCAIGRID_HOST/v1/modules/scaiskills/bindings" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"skill_id\": \"$SKILL_ID\",
    \"version\": \"0.1.0\",
    \"scope_type\": \"workspace\",
    \"scope_id\": \"$WORKSPACE_ID\"
  }"
```

Because the manifest declared no permissions and no secrets, `pending_grants` is `false` and the binding is active immediately.

## 5. Resolve the scope

This is what the runtime calls per turn.

```bash
curl -X POST "$SCAIGRID_HOST/v1/modules/scaiskills/resolve" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"scope_type\": \"workspace\",
    \"workspace_id\": \"$WORKSPACE_ID\"
  }"
```

You get back the lightweight manifest list — slug, version, description, triggers — and a `cache_ttl_ms` of 60 000. The full body is fetched only when the model decides to call `skills.view` on the slug.

## What just happened

- `POST /skills` wrote a `mod_scaiskills_skills` row owned by your workspace.
- `POST /skills/{slug}/versions` validated the bundle, deduped against existing content hashes, stored at `s3://.../scaiskills/{skill_id}/{content_hash}.tar.gz`, and persisted the parsed manifest on the version row.
- `POST /bindings` resolved `0.1.0` to a concrete published version, walked its `requires.skills` (none here), built an empty lockfile, and flipped `pending_grants` to `false`.
- `POST /resolve` looked up active, non-pending bindings for the workspace, deduped by skill_id with user > channel > workspace precedence, and returned the lightweight manifests. Result is now cached in Redis for 60 seconds.

## Next

- [Architecture](./concepts/architecture) — the full request flow and where state lives.
- [Bindings and resolution](./concepts/bindings-and-resolution) — scope precedence, lockfiles, pending grants.
- [Publish a skill](./tutorials/publish-a-skill) — the real bundle layout, manifest fields, references directory.
- [Bind with permissions and secrets](./tutorials/bind-with-grants) — when the binding sits in pending_grants and how to clear it.
