MENU navbar-image

Introduction

The registry for AI agent skills (SKILL.md bundles) — browse them, download them as a zip, or install them via the revenexx CLI into any supported agent; sourced from connected Git repositories. Agent-agnostic, not Claude-only.

The public Skill Registry API lets clients browse skills, fetch their details and versions,
and download a skill bundle as a zip.

All endpoints require a Zitadel-issued OIDC JWT, sent as a bearer token. Authorization is by
**Zitadel project roles** mapped to API planes (`require.plane` middleware). The public API
(`/v1`) requires any role in the Revenue Cloud project (read access: browse, view detail,
download).

**Visibility is scoped to the caller's Zitadel organization:** public skills are visible to
every organization, while private skills are only visible to the owning organization. A
skill's visibility is derived from its manifest during a repo scan and cannot be changed
through the API. There is no `X-Tenant-Id` header in this service — the organization is
resolved from the token.

First-party UI endpoints (GitHub connection, repository management) live in the **internal**
plane (`/v1/internal`) and are documented in a separate spec. Platform-operator endpoints
live in the **admin** plane (`/v1/admin`).

**Base URL:** `/v1`
**Versioning:** URL path prefix per [ADR-0036](https://atlas.revenexx.dev/adr/adr-0036-api-versioning-strategy).

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {ZITADEL_JWT}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

Authenticate with a Zitadel-issued OIDC JWT as a bearer token. The public plane (/v1) requires any role in the Revenue Cloud project (read access). A skill's visibility is resolved against the caller's Zitadel organization.

Skills

List skills visible to the calling organisation.

requires authentication

Optional ?q= filters by name/title/description/tags. Visibility is scoped to the caller's Zitadel organization: public skills are returned to everyone, private skills only to the owning organization.

Example request:
curl --request GET \
    --get "https://skills.revenexx.ai/api/v1/skills?q=invoice&category=finance&tags=erp%2Cinvoice" \
    --header "Authorization: Bearer {ZITADEL_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://skills.revenexx.ai/api/v1/skills"
);

const params = {
    "q": "invoice",
    "category": "finance",
    "tags": "erp,invoice",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {ZITADEL_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "vendor": "revenexx",
            "name": "invoice-sync",
            "slug": "revenexx/invoice-sync",
            "title": "Invoice Sync",
            "description": "Sync invoices between Business Central and the data warehouse.",
            "visibility": "public",
            "category": "finance",
            "tags": [
                "invoice",
                "erp"
            ],
            "latest_version": "1.2.0",
            "latest_download_url": "https://skills.rvnxx.test/api/v1/skills/revenexx/skills-catalog/invoice-sync/versions/1.2.0/download"
        }
    ]
}
 

Example response (401, Missing or invalid token):


{
    "message": "Unauthenticated."
}
 

Example response (403, No role in the public plane):


{
    "message": "Token carries no roles in the public project."
}
 

Request      

GET api/v1/skills

Headers

Authorization        

Example: Bearer {ZITADEL_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

q   string  optional    

Optional search over name/title/description/tags. Example: invoice

category   string  optional    

Optional exact category filter. Example: finance

tags   string  optional    

Optional comma-separated tag filter; a skill matching any listed tag is returned. Example: erp,invoice

List the distinct, non-empty skill categories visible to the caller's organisation. Powers the category filter on the browse page.

requires authentication

Example request:
curl --request GET \
    --get "https://skills.revenexx.ai/api/v1/skills/categories" \
    --header "Authorization: Bearer {ZITADEL_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://skills.revenexx.ai/api/v1/skills/categories"
);

const headers = {
    "Authorization": "Bearer {ZITADEL_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        "data",
        "devops",
        "finance"
    ]
}
 

Request      

GET api/v1/skills/categories

Headers

Authorization        

Example: Bearer {ZITADEL_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

List the distinct tags across skills visible to the caller's organisation.

requires authentication

Powers the multi-select tag filter on the browse page.

Example request:
curl --request GET \
    --get "https://skills.revenexx.ai/api/v1/skills/tags" \
    --header "Authorization: Bearer {ZITADEL_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://skills.revenexx.ai/api/v1/skills/tags"
);

const headers = {
    "Authorization": "Bearer {ZITADEL_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        "ai",
        "cli",
        "erp",
        "invoice"
    ]
}
 

Request      

GET api/v1/skills/tags

Headers

Authorization        

Example: Bearer {ZITADEL_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

Show a single skill (by vendor + name) with its versions.

requires authentication

Example request:
curl --request GET \
    --get "https://skills.revenexx.ai/api/v1/skills/revenexx/skills-catalog/invoice-sync" \
    --header "Authorization: Bearer {ZITADEL_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://skills.revenexx.ai/api/v1/skills/revenexx/skills-catalog/invoice-sync"
);

const headers = {
    "Authorization": "Bearer {ZITADEL_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": {
        "vendor": "revenexx",
        "repo": "skills-catalog",
        "name": "invoice-sync",
        "slug": "revenexx/skills-catalog/invoice-sync",
        "title": "Invoice Sync",
        "description": "Sync invoices between Business Central and the data warehouse.",
        "visibility": "public",
        "category": "finance",
        "tags": [
            "invoice",
            "erp"
        ],
        "keywords": [
            "invoice",
            "business-central"
        ],
        "authors": [
            {
                "name": "Jane Doe",
                "email": "jane@revenexx.com"
            }
        ],
        "homepage": "https://docs.revenexx.com/skills/invoice-sync",
        "license": "MIT",
        "targets": [
            "claude-code",
            "cursor"
        ],
        "allowed_tools": [
            "Bash",
            "Read"
        ],
        "latest_version": "1.2.0",
        "readme": "# Invoice Sync\n\nSyncs invoices…",
        "latest_download_url": "https://skills.rvnxx.test/api/v1/skills/revenexx/skills-catalog/invoice-sync/versions/1.2.0/download",
        "versions": [
            {
                "version": "1.2.0",
                "description": "Sync invoices between Business Central and the data warehouse.",
                "artifact_hash": "sha256:7f3b1c9a2e4d5f6071829304a5b6c7d8e9f0a1b2c3d4e5f60718293a4b5c6d7e",
                "artifact_bytes": 20480,
                "registered_at": "2026-06-20T09:12:00Z"
            }
        ]
    }
}
 

Example response (404, Skill not found or not visible to caller):


{
    "message": "Not Found."
}
 

Request      

GET api/v1/skills/{vendor}/{repo}/{name}

Headers

Authorization        

Example: Bearer {ZITADEL_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

vendor   string     

The skill vendor (GitHub owner). Example: revenexx

repo   string     

The repository the skill lives in. Example: skills-catalog

name   string     

The skill name. Example: invoice-sync

Stream the zip artifact for a specific version of a skill.

requires authentication

Serves both the CLI install and a direct zip download for any AI chat/agent (the registry is agent-agnostic, not Claude-only). The response body is the raw skill bundle streamed as an application/zip attachment named {name}-{version}.zip — it is not a JSON document.

Example request:
curl --request GET \
    --get "https://skills.revenexx.ai/api/v1/skills/revenexx/skills-catalog/invoice-sync/versions/1.2.0/download" \
    --header "Authorization: Bearer {ZITADEL_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://skills.revenexx.ai/api/v1/skills/revenexx/skills-catalog/invoice-sync/versions/1.2.0/download"
);

const headers = {
    "Authorization": "Bearer {ZITADEL_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, Skill bundle):


Binary `application/zip` stream (Content-Disposition: attachment; filename="{name}-{version}.zip"). Not a JSON document.
 

Example response (404, Skill or version not found / not visible to caller):


{
    "message": "Not Found."
}
 

Request      

GET api/v1/skills/{vendor}/{repo}/{name}/versions/{version}/download

Headers

Authorization        

Example: Bearer {ZITADEL_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

vendor   string     

The skill vendor (GitHub owner). Example: revenexx

repo   string     

The repository the skill lives in. Example: skills-catalog

name   string     

The skill name. Example: invoice-sync

version   string     

The skill version to download. Example: 1.2.0

Show a single version of a skill, including that version's README.

requires authentication

Unlike the skill detail endpoint (whose readme is always the latest version's), this returns the README and metadata of the exact version requested, plus parent-skill context (title, latest_version, is_latest) so the UI can flag when an older version is being viewed.

Example request:
curl --request GET \
    --get "https://skills.revenexx.ai/api/v1/skills/revenexx/skills-catalog/invoice-sync/versions/1.2.0" \
    --header "Authorization: Bearer {ZITADEL_JWT}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://skills.revenexx.ai/api/v1/skills/revenexx/skills-catalog/invoice-sync/versions/1.2.0"
);

const headers = {
    "Authorization": "Bearer {ZITADEL_JWT}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": {
        "version": "1.2.0",
        "description": "Sync invoices between Business Central and the data warehouse.",
        "readme": "# Invoice Sync\n\nSyncs invoices…",
        "visibility": "public",
        "allowed_tools": [
            "Bash",
            "Read"
        ],
        "artifact_hash": "sha256:7f3b1c9a2e4d5f6071829304a5b6c7d8e9f0a1b2c3d4e5f60718293a4b5c6d7e",
        "artifact_bytes": 20480,
        "source_ref": "refs/tags/v1.2.0",
        "registered_at": "2026-06-20T09:12:00Z",
        "title": "Invoice Sync",
        "latest_version": "1.2.0",
        "is_latest": true
    }
}
 

Example response (404, Skill or version not found / not visible to caller):


{
    "message": "Not Found."
}
 

Request      

GET api/v1/skills/{vendor}/{repo}/{name}/versions/{version}

Headers

Authorization        

Example: Bearer {ZITADEL_JWT}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

vendor   string     

The skill vendor (GitHub owner). Example: revenexx

repo   string     

The repository the skill lives in. Example: skills-catalog

name   string     

The skill name. Example: invoice-sync

version   string     

The skill version. Example: 1.2.0