Skip to content
POST/api/v1/skills/publish

Publish a skill manifest (supply-chain scanned)

Publishes a signed SkillManifest to the caller's org skill registry (per-org RLS isolation). The manifest schema is validated (422 MANIFEST_INVALID on bad shape) and the promptTemplate + description are run through a supply-chain security scan before any storage write. Default (flag OFF): any `flagged` content scan hard-rejects with 422 (MANIFEST_MALICIOUS). With EVALGUARD_SKILL_PUBLISH_HITL=1 the gate is severity-tiered — a CRITICAL finding still hard-rejects 422 (never routed to human review), while a review-tier / non-critical block is HELD for a durable, crypto-bound human approval (202 pending_approval, nothing persisted); re-present the approvalId as `hitlApprovalId` to resume. With EVALGUARD_ENFORCE_SUPPLYCHAIN_SKILL=1 an additional hardening scan (zip-slip / bundled-binary / auto-load / over-broad grant) can hard-deny with 422 (SKILL_HARDENING_BLOCKED). Signature digest mismatch returns 422 (MANIFEST_VERIFY_FAILED). Publishing requires the `gateway:create` permission — owner, admin or editor (an API key is evaluated at its scope-capped role); a `member`- or `viewer`-role user is refused 403. Rate limit 30/min/org; request bodies up to 4 MiB.

Authentication

Send Authorization: Bearer YOUR_API_KEY on every request. Generate API keys at /dashboard/settings/api-keys.

Request body required

Example

{
  "orgId": "00000000-0000-0000-0000-000000000000",
  "manifest": {
    "name": "string",
    "version": "string",
    "displayName": "string",
    "description": "string",
    "license": "string",
    "author": "string",
    "sourceUrl": "https://example.com",
    "tags": [
      "string"
    ],
    "promptTemplate": "string",
    "inputs": [
      {}
    ],
    "outputs": [
      {}
    ],
    "requestedPermissions": [
      "fs:read"
    ],
    "permissionScopes": {
      "tools": [
        "string"
      ],
      "domains": [
        "string"
      ]
    },
    "sandbox": "none",
    "dependencies": [
      {}
    ],
    "examples": [
      {}
    ],
    "publishedAt": "string",
    "signature": {
      "digest": "<Hex content digest.>",
      "ed25519": "string",
      "publicKeyId": "string"
    }
  },
  "ed25519PublicKeyHex": "<Optional Ed25519 verifier — caller's pub>",
  "hitlApprovalId": "00000000-0000-0000-0000-000000000000"
}
Schema
{
  "application/json": {
    "schema": {
      "type": "object",
      "required": [
        "orgId",
        "manifest"
      ],
      "additionalProperties": false,
      "properties": {
        "orgId": {
          "type": "string",
          "format": "uuid"
        },
        "manifest": {
          "type": "object",
          "description": "The SkillManifest to publish (R6 Tier-2). Includes a content-hash signature.",
          "required": [
            "name",
            "version",
            "displayName",
            "description",
            "license",
            "author",
            "tags",
            "promptTemplate",
            "inputs",
            "outputs",
            "requestedPermissions",
            "sandbox",
            "publishedAt",
            "signature"
          ],
          "properties": {
            "name": {
              "type": "string",
              "minLength": 1,
              "maxLength": 256
            },
            "version": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            },
            "displayName": {
              "type": "string",
              "minLength": 1,
              "maxLength": 256
            },
            "description": {
              "type": "string",
              "minLength": 1,
              "maxLength": 8192
            },
            "license": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            },
            "author": {
              "type": "string",
              "minLength": 1,
              "maxLength": 256
            },
            "sourceUrl": {
              "type": "string",
              "format": "uri",
              "maxLength": 2048
            },
            "tags": {
              "type": "array",
              "maxItems": 64,
              "items": {
                "type": "string",
                "maxLength": 64
              }
            },
            "promptTemplate": {
              "type": "string",
              "minLength": 1,
              "maxLength": 32768
            },
            "inputs": {
              "type": "array",
              "maxItems": 64,
              "items": {
                "type": "object",
                "additionalProperties": {}
              }
            },
            "outputs": {
              "type": "array",
              "maxItems": 64,
              "items": {
                "type": "object",
                "additionalProperties": {}
              }
            },
            "requestedPermissions": {
              "type": "array",
              "maxItems": 16,
              "items": {
                "type": "string",
                "enum": [
                  "fs:read",
                  "fs:write",
                  "net:*",
                  "net:domain",
                  "exec:shell",
                  "tool:*",
                  "tool:specific",
                  "secrets:read"
                ]
              }
            },
            "permissionScopes": {
              "type": "object",
              "properties": {
                "tools": {
                  "type": "array",
                  "maxItems": 64,
                  "items": {
                    "type": "string",
                    "maxLength": 256
                  }
                },
                "domains": {
                  "type": "array",
                  "maxItems": 64,
                  "items": {
                    "type": "string",
                    "maxLength": 256
                  }
                }
              },
              "additionalProperties": false
            },
            "sandbox": {
              "type": "string",
              "enum": [
                "none",
                "isolated",
                "no-network"
              ]
            },
            "dependencies": {
              "type": "array",
              "maxItems": 32,
              "items": {
                "type": "object",
                "additionalProperties": {}
              }
            },
            "examples": {
              "type": "array",
              "maxItems": 32,
              "items": {
                "type": "object",
                "additionalProperties": {}
              }
            },
            "publishedAt": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            },
            "signature": {
              "type": "object",
              "required": [
                "digest"
              ],
              "properties": {
                "digest": {
                  "type": "string",
                  "description": "Hex content digest.",
                  "minLength": 32,
                  "maxLength": 256
                },
                "ed25519": {
                  "type": "string",
                  "maxLength": 512
                },
                "publicKeyId": {
                  "type": "string",
                  "maxLength": 256
                }
              },
              "additionalProperties": false
            }
          },
          "additionalProperties": true
        },
        "ed25519PublicKeyHex": {
          "type": "string",
          "description": "Optional Ed25519 verifier — caller's public-key bytes (hex).",
          "maxLength": 512
        },
        "hitlApprovalId": {
          "type": "string",
          "format": "uuid",
          "description": "When EVALGUARD_SKILL_PUBLISH_HITL is on: the durable approval id to RESUME a review-tier publish an operator approved."
        }
      }
    }
  }
}

Response

201 example

{
  "success": true
}

All status codes

201Published. Returns the stored manifest identity tuple {ok,name,version,digest,publishedAt}.
202Held for human review (EVALGUARD_SKILL_PUBLISH_HITL on, review-tier verdict). Nothing persisted; returns {pending,approvalId,status:'pending_approval'}.
400(no description)
401(no description)
403Forbidden — skill-publish approval not authorized for this caller or not resumable.
409Approval refused — the manifest changed after approval (crypto-binding mismatch).
413Payload Too Large — SKILL_CODE_TOO_LARGE.
422Manifest rejected — MANIFEST_INVALID (bad shape), MANIFEST_MALICIOUS (failed security scan), MANIFEST_VERIFY_FAILED (signature digest mismatch), or SKILL_HARDENING_BLOCKED.
429(no description)
500Internal Server Error — SKILL_PUBLISH_ERROR.
503Skills backend unavailable or HITL approval could not be recorded (fails closed — not published).

Code samples

cURL

curl -X POST \
  https://evalguard.ai/api/v1/skills/publish \
  -H "Authorization: Bearer $EVALGUARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "orgId": "00000000-0000-0000-0000-000000000000", "manifest": { "name": "string", "version": "string", "displayName": "string", "description": "string", "license": "string", "author": "string", "sourceUrl": "https://example.com", "tags": [ "string" ], "promptTemplate": "string", "inputs": [ {} ], "outputs": [ {} ], "requestedPermissions": [ "fs:read" ], "permissionScopes": { "tools": [ "string" ], "domains": [ "string" ] }, "sandbox": "none", "dependencies": [ {} ], "examples": [ {} ], "publishedAt": "string", "signature": { "digest": "<Hex content digest.>", "ed25519": "string", "publicKeyId": "string" } }, "ed25519PublicKeyHex": "<Optional Ed25519 verifier — caller'\''s pub>", "hitlApprovalId": "00000000-0000-0000-0000-000000000000" }'

TypeScript

// The TypeScript SDK (@evalguard/sdk) exposes TYPED methods — runEval,
// getEval, runSecurityScan, checkFirewall, … — not a generic request().
// For an arbitrary endpoint, call it directly:

const res = await fetch("https://evalguard.ai/api/v1/skills/publish", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.EVALGUARD_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "orgId": "00000000-0000-0000-0000-000000000000",
    "manifest": {
      "name": "string",
      "version": "string",
      "displayName": "string",
      "description": "string",
      "license": "string",
      "author": "string",
      "sourceUrl": "https://example.com",
      "tags": [
        "string"
      ],
      "promptTemplate": "string",
      "inputs": [
        {}
      ],
      "outputs": [
        {}
      ],
      "requestedPermissions": [
        "fs:read"
      ],
      "permissionScopes": {
        "tools": [
          "string"
        ],
        "domains": [
          "string"
        ]
      },
      "sandbox": "none",
      "dependencies": [
        {}
      ],
      "examples": [
        {}
      ],
      "publishedAt": "string",
      "signature": {
        "digest": "<Hex content digest.>",
        "ed25519": "string",
        "publicKeyId": "string"
      }
    },
    "ed25519PublicKeyHex": "<Optional Ed25519 verifier — caller's pub>",
    "hitlApprovalId": "00000000-0000-0000-0000-000000000000"
  }),
});
console.log(res.status, await res.json());

Python

# The Python SDK (pip install evalguardai) exposes TYPED methods on
# EvalGuardClient — run_eval, get_eval, … — not a generic request().
# For an arbitrary endpoint, call it directly:

import os
import requests

headers = {"Authorization": f"Bearer {os.environ['EVALGUARD_API_KEY']}"}
headers["Content-Type"] = "application/json"

response = requests.request(
    "POST",
    "https://evalguard.ai/api/v1/skills/publish",
    headers=headers,
    json={
    "orgId": "00000000-0000-0000-0000-000000000000",
    "manifest": {
        "name": "string",
        "version": "string",
        "displayName": "string",
        "description": "string",
        "license": "string",
        "author": "string",
        "sourceUrl": "https://example.com",
        "tags": [
            "string"
        ],
        "promptTemplate": "string",
        "inputs": [
            {}
        ],
        "outputs": [
            {}
        ],
        "requestedPermissions": [
            "fs:read"
        ],
        "permissionScopes": {
            "tools": [
                "string"
            ],
            "domains": [
                "string"
            ]
        },
        "sandbox": "none",
        "dependencies": [
            {}
        ],
        "examples": [
            {}
        ],
        "publishedAt": "string",
        "signature": {
            "digest": "<Hex content digest.>",
            "ed25519": "string",
            "publicKeyId": "string"
        }
    },
    "ed25519PublicKeyHex": "<Optional Ed25519 verifier — caller's pub>",
    "hitlApprovalId": "00000000-0000-0000-0000-000000000000"
},
)
print(response.status_code, response.json())

Go

package main

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"strings"
)

func main() {
	body := strings.NewReader(`{"orgId":"00000000-0000-0000-0000-000000000000","manifest":{"name":"string","version":"string","displayName":"string","description":"string","license":"string","author":"string","sourceUrl":"https://example.com","tags":["string"],"promptTemplate":"string","inputs":[{}],"outputs":[{}],"requestedPermissions":["fs:read"],"permissionScopes":{"tools":["string"],"domains":["string"]},"sandbox":"none","dependencies":[{}],"examples":[{}],"publishedAt":"string","signature":{"digest":"<Hex content digest.>","ed25519":"string","publicKeyId":"string"}},"ed25519PublicKeyHex":"<Optional Ed25519 verifier — caller's pub>","hitlApprovalId":"00000000-0000-0000-0000-000000000000"}`)
	req, _ := http.NewRequestWithContext(context.Background(), "POST", "https://evalguard.ai/api/v1/skills/publish", body)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("EVALGUARD_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil { panic(err) }
	defer resp.Body.Close()
	fmt.Println(resp.Status)
}

Errors

400401403409413422429500503

Other Security endpoints