/api/v1/skills/publishPublish 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
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)
}