Skip to main content
GET
/
v1
/
recipes
Recipe Registry API
curl --request GET \
  --url http://127.0.0.1:8765/v1/recipes \
  --header 'Content-Type: application/json' \
  --data '
{
  "bundle": "<string>",
  "force": true
}
'
import requests

url = "http://127.0.0.1:8765/v1/recipes"

payload = {
    "bundle": "<string>",
    "force": True
}
headers = {"Content-Type": "application/json"}

response = requests.get(url, json=payload, headers=headers)

print(response.text)
const options = {
  method: 'GET',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({bundle: '<string>', force: true})
};

fetch('http://127.0.0.1:8765/v1/recipes', options)
  .then(res => res.json())
  .then(res => console.log(res))
  .catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_PORT => "8765",
  CURLOPT_URL => "http://127.0.0.1:8765/v1/recipes",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_POSTFIELDS => json_encode([
    'bundle' => '<string>',
    'force' => true
  ]),
  CURLOPT_HTTPHEADER => [
    "Content-Type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "http://127.0.0.1:8765/v1/recipes"

	payload := strings.NewReader("{\n  \"bundle\": \"<string>\",\n  \"force\": true\n}")

	req, _ := http.NewRequest("GET", url, payload)

	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("http://127.0.0.1:8765/v1/recipes")
  .header("Content-Type", "application/json")
  .body("{\n  \"bundle\": \"<string>\",\n  \"force\": true\n}")
  .asString();
require 'uri'
require 'net/http'

url = URI("http://127.0.0.1:8765/v1/recipes")

http = Net::HTTP.new(url.host, url.port)

request = Net::HTTP::Get.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n  \"bundle\": \"<string>\",\n  \"force\": true\n}"

response = http.request(request)
puts response.read_body

Recipe Registry API

HTTP API endpoints for the recipe registry server started via praisonai serve registry.

Base URL + Playground

# Start registry server
praisonai serve registry --port 7777
Base URL: http://127.0.0.1:7777

Endpoints

GET /healthz

Health check endpoint.
none
none
No parameters required.
curl http://127.0.0.1:7777/healthz
import requests

response = requests.get("http://127.0.0.1:7777/healthz")
health = response.json()
print(f"Status: {health['status']}")
print(f"Auth required: {health['auth_required']}")
const response = await fetch("http://127.0.0.1:7777/healthz");
const health = await response.json();
console.log(`Status: ${health.status}`);
Response:
{
  "status": "healthy",
  "auth_required": true,
  "read_only": false,
  "version": "1.0.0"
}

GET /v1/recipes

List all recipes in the registry.
tags
string
Filter by tags (comma-separated)
limit
integer
Maximum number of results (default: 50)
offset
integer
Offset for pagination (default: 0)
curl http://127.0.0.1:7777/v1/recipes
import requests

response = requests.get("http://127.0.0.1:7777/v1/recipes")
data = response.json()
for recipe in data["recipes"]:
    print(f"- {recipe['name']} v{recipe['version']}")
const response = await fetch("http://127.0.0.1:7777/v1/recipes");
const { recipes } = await response.json();
recipes.forEach(r => console.log(`- ${r.name} v${r.version}`));
Response:
{
  "recipes": [
    {
      "name": "my-agent",
      "version": "1.0.0",
      "description": "A helpful AI agent",
      "tags": ["agent", "assistant"]
    }
  ],
  "total": 1
}

GET /v1/recipes/

Get information about a specific recipe.
name
string
required
Recipe name
curl http://127.0.0.1:7777/v1/recipes/my-agent
import requests

response = requests.get("http://127.0.0.1:7777/v1/recipes/my-agent")
info = response.json()
print(f"Latest: {info['latest']}")
print(f"Versions: {list(info['versions'].keys())}")
Response:
{
  "name": "my-agent",
  "latest": "1.0.0",
  "versions": {
    "1.0.0": {
      "checksum": "sha256:abc123...",
      "published_at": "2024-01-15T10:30:00Z"
    }
  }
}

GET /v1/recipes//

Get information about a specific version.
name
string
required
Recipe name
version
string
required
Version string (e.g., “1.0.0”)
curl http://127.0.0.1:7777/v1/recipes/my-agent/1.0.0
import requests

response = requests.get("http://127.0.0.1:7777/v1/recipes/my-agent/1.0.0")
version_info = response.json()
print(f"Checksum: {version_info['checksum']}")
Response:
{
  "name": "my-agent",
  "version": "1.0.0",
  "checksum": "sha256:abc123...",
  "published_at": "2024-01-15T10:30:00Z",
  "manifest": {
    "description": "A helpful AI agent",
    "tags": ["agent", "assistant"],
    "author": "praison"
  }
}

GET /v1/recipes///download

Download a recipe bundle.
name
string
required
Recipe name
version
string
required
Version string
If-None-Match
string
ETag for conditional download
curl -o my-agent-1.0.0.praison \
  http://127.0.0.1:7777/v1/recipes/my-agent/1.0.0/download
import requests

response = requests.get(
    "http://127.0.0.1:7777/v1/recipes/my-agent/1.0.0/download"
)
with open("my-agent-1.0.0.praison", "wb") as f:
    f.write(response.content)
Response: Binary .praison bundle file Headers:
  • Content-Type: application/gzip
  • ETag: "sha256:abc123..."

POST /v1/recipes//

Publish a recipe bundle. Requires authentication if token is configured.
name
string
required
Recipe name
version
string
required
Version string
Authorization
string
Bearer token for authentication
bundle
string
required
Base64-encoded .praison bundle
force
boolean
Overwrite existing version (default: false)
# Encode bundle as base64
BUNDLE=$(base64 -i my-agent-1.0.0.praison)

curl -X POST http://127.0.0.1:7777/v1/recipes/my-agent/1.0.0 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $PRAISONAI_REGISTRY_TOKEN" \
  -d "{\"bundle\": \"$BUNDLE\"}"
import requests
import base64
import os

# Read and encode bundle
with open("my-agent-1.0.0.praison", "rb") as f:
    bundle_b64 = base64.b64encode(f.read()).decode()

response = requests.post(
    "http://127.0.0.1:7777/v1/recipes/my-agent/1.0.0",
    headers={
        "Authorization": f"Bearer {os.environ['PRAISONAI_REGISTRY_TOKEN']}"
    },
    json={"bundle": bundle_b64}
)
result = response.json()
print(f"Published: {result['name']}@{result['version']}")
Response:
{
  "ok": true,
  "name": "my-agent",
  "version": "1.0.0",
  "checksum": "sha256:abc123...",
  "published_at": "2024-01-15T10:30:00Z"
}

DELETE /v1/recipes//

Delete a recipe version. Requires authentication if token is configured.
name
string
required
Recipe name
version
string
required
Version string
Authorization
string
Bearer token for authentication
curl -X DELETE http://127.0.0.1:7777/v1/recipes/my-agent/1.0.0 \
  -H "Authorization: Bearer $PRAISONAI_REGISTRY_TOKEN"
import requests
import os

response = requests.delete(
    "http://127.0.0.1:7777/v1/recipes/my-agent/1.0.0",
    headers={
        "Authorization": f"Bearer {os.environ['PRAISONAI_REGISTRY_TOKEN']}"
    }
)
print(f"Deleted: {response.status_code == 200}")
Response:
{
  "ok": true,
  "message": "Deleted my-agent@1.0.0"
}

GET /v1/search

Search recipes by query.
q
string
required
Search query (matches name, description, tags)
curl "http://127.0.0.1:7777/v1/search?q=agent"
import requests

response = requests.get(
    "http://127.0.0.1:7777/v1/search",
    params={"q": "agent"}
)
results = response.json()
for r in results:
    print(f"- {r['name']}: {r['description']}")
const response = await fetch("http://127.0.0.1:7777/v1/search?q=agent");
const results = await response.json();
results.forEach(r => console.log(`- ${r.name}: ${r.description}`));
Response:
[
  {
    "name": "my-agent",
    "version": "1.0.0",
    "description": "A helpful AI agent",
    "tags": ["agent", "assistant"]
  }
]

Authentication

When the server is started with --token, write operations require authentication:
# Start server with token
praisonai serve registry --token $PRAISONAI_REGISTRY_TOKEN
Header format:
Authorization: Bearer <token>
Protected endpoints:
  • POST /v1/recipes/{name}/{version} - Publish
  • DELETE /v1/recipes/{name}/{version} - Delete
Unprotected endpoints:
  • GET /healthz - Health check
  • GET /v1/recipes - List
  • GET /v1/recipes/{name} - Info
  • GET /v1/recipes/{name}/{version} - Version info
  • GET /v1/recipes/{name}/{version}/download - Download
  • GET /v1/search - Search

Errors

StatusDescription
200Success
201Created (publish)
304Not Modified (ETag match)
400Invalid request
401Unauthorized (missing/invalid token)
404Recipe or version not found
409Conflict (version exists, use force)
500Server error
Error Response:
{
  "error": "Recipe not found: my-agent",
  "code": 404
}

CLI Equivalent

# List recipes
praisonai recipe list --registry http://localhost:7777

# Search recipes
praisonai recipe search agent --registry http://localhost:7777

# Publish recipe
praisonai recipe publish ./my-recipe \
  --registry http://localhost:7777 \
  --token $TOKEN

# Pull recipe
praisonai recipe pull my-agent \
  --registry http://localhost:7777 \
  -o ./recipes