REST API Reference
PuppyOne provides a complete REST API covering content management, agent configuration, data sync, and more.
Basic information
Base URL
Production:
https://api.puppyone.ai/api/v1Self-hosted:
http://localhost:9090/api/v1Authentication
All API requests must include authentication headers. Two authentication methods are supported:
JWT token, for user authentication:
Authorization: Bearer <access_token>Access key, for machine or agent authentication:
Authorization: Bearer cli_xxxCommon parameters
Some list endpoints support these query parameters:
| Parameter | Type | Description |
|---|---|---|
project_id | string | Project ID, required for most endpoints |
org_id | string | Organization ID |
1. Authentication
Get public config
GET /auth/configReturns public Supabase configuration, URL and anon key, for client-side Realtime connections.
Log in
POST /auth/loginRequest body:
{
"email": "[email protected]",
"password": "your-password"
}Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "v1.xxx",
"expires_in": 3600
}Refresh token
POST /auth/refreshRequest body:
{
"refresh_token": "v1.xxx"
}Initialize user
POST /auth/initializeAn idempotent operation that creates the user's profile and default organization. Call this after the first login.
2. Organizations
List organizations
GET /organizations/Returns all organizations the current user belongs to.
Create organization
POST /organizations/Request body:
{
"name": "My Team",
"description": "Team description"
}Get organization details
GET /organizations/{org_id}Update organization
PUT /organizations/{org_id}Delete organization
DELETE /organizations/{org_id}Member management
GET /organizations/{org_id}/members # List members
PUT /organizations/{org_id}/members/{user_id}/role # Update role
DELETE /organizations/{org_id}/members/{user_id} # Remove member
POST /organizations/{org_id}/leave # Leave organizationInvitations
POST /organizations/{org_id}/invite # Send invitation
GET /organizations/{org_id}/invitations # Show pending invitations
POST /organizations/invitations/{token}/accept # Accept invitation3. Projects
List projects
GET /projects/?org_id={org_id}Create project
POST /projects/Request body:
{
"name": "Product Knowledge Base",
"description": "Stores product-related data",
"org_id": "org_xxx"
}Get project details
GET /projects/{project_id}Update project
PUT /projects/{project_id}Delete project
DELETE /projects/{project_id}Project dashboard
GET /projects/{project_id}/dashboardReturns aggregated project state, including node counts, connection status, and sync progress.
Project members
GET /projects/{project_id}/members # List members
POST /projects/{project_id}/members # Add member
PUT /projects/{project_id}/members/{user_id}/role # Update role
DELETE /projects/{project_id}/members/{user_id} # Remove memberSeed default content
POST /projects/{project_id}/seedCreates default sample content for the project.
4. Content
Content is PuppyOne's core data layer. All operations use path-based addressing under the /content/{project_id}/ prefix. Three content types are supported: json, markdown, and file.
All responses are wrapped in ApiResponse:
{"success": true, "data": {...}, "message": "..."}List directory
GET /content/{project_id}/ls?path={path}| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | No | Directory path. Empty or omitted returns the root |
Read file
GET /content/{project_id}/cat?path={path}Returns the file content. JSON nodes return content, Markdown nodes return content_text.
File metadata
GET /content/{project_id}/stat?path={path}Returns metadata including type, name, content_hash, mime_type, and children_count.
Recursive tree
GET /content/{project_id}/tree?path={path}&max_depth=-1| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | No | Root path for the tree |
max_depth | integer | No | Maximum depth. -1 for unlimited (default) |
List trash
GET /content/{project_id}/trashReturns all items in the trash bin.
Write file
POST /content/{project_id}/writeRequest body:
{
"path": "reports/weekly.json",
"content": { "title": "Weekly Report", "status": "draft" },
"node_type": "json",
"message": "Create weekly report"
}| Field | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File path |
content | any | Yes | File content (object for JSON, string for Markdown) |
node_type | string | Yes | json, markdown, or file |
message | string | No | Commit message |
Create directory
POST /content/{project_id}/mkdirRequest body:
{
"path": "reports/2025"
}Move / rename
POST /content/{project_id}/mvRequest body:
{
"old_path": "reports/weekly.json",
"new_path": "archive/weekly-old.json",
"message": "Archive old report"
}Delete
POST /content/{project_id}/rmRequest body:
{
"path": "reports/weekly.json",
"permanent": false
}When permanent is false (default), the file is moved to trash. Set true to delete permanently.
Restore from trash
POST /content/{project_id}/restoreRequest body:
{
"trash_path": ".trash/weekly.json",
"original_path": "reports/weekly.json"
}Bulk write
POST /content/{project_id}/bulk-writeRequest body:
{
"files": [
{ "path": "config/a.json", "content": { "key": "value" }, "node_type": "json" },
{ "path": "docs/readme.md", "content": "# Hello", "node_type": "markdown" }
],
"message": "Initial setup"
}Version history
GET /content/{project_id}/versions?path={path}&limit=50&since_version=0| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File path |
limit | integer | No | Max versions to return (default 50) |
since_version | integer | No | Return versions after this version number |
Version list response:
[
{
"version": 3,
"actor_type": "user",
"actor_id": "user_xxx",
"created_at": "2025-01-03T00:00:00Z",
"message": "Updated config"
},
{
"version": 2,
"actor_type": "agent",
"actor_id": "agent_xxx",
"created_at": "2025-01-02T00:00:00Z"
}
]Version content
GET /content/{project_id}/version-content?path={path}&version={version}Returns the content of a file at a specific version.
Diff
GET /content/{project_id}/diff?v1={v1}&v2={v2}Compare two versions of the project.
Rollback
POST /content/{project_id}/rollbackRequest body:
{
"target_version": 5
}Rolls the project back to the specified version.
Audit logs
GET /nodes/{path}/audit-logsReturns the full operation history for a node, who did what and when.
Response:
[
{
"action": "update",
"actor_type": "user",
"actor_id": "user_xxx",
"path": "reports/weekly.json",
"timestamp": "2025-01-01T12:00:00Z",
"details": {}
}
]5. Tables
Table APIs are a supplemental operation layer for structured JSON data. They use JSON Pointer paths to locate and modify data, but the primary data model in PuppyOne is still the Content Node.
List tables
GET /tables/?org_id={org_id}Get table
GET /tables/{table_id}Create table
POST /tables/Update table
PUT /tables/{table_id}Delete table
DELETE /tables/{table_id}Get data
GET /tables/{table_id}/data?path=/products| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | No | JSON Pointer path, defaults to / |
Create data
POST /tables/{table_id}/dataRequest body:
{
"path": "/products",
"data": {
"id": "SKU-003",
"name": "New Widget",
"price": 129.99
}
}Update data
PUT /tables/{table_id}/dataRequest body:
{
"path": "/products/0/price",
"value": 89.99
}Delete data
DELETE /tables/{table_id}/data?path=/products/06. Connection management
Connections are PuppyOne's unified integration abstraction, covering agents, MCP endpoints, sandbox endpoints, sync connections, and more.
List connections
GET /access/?project_id={project_id}Get connection
GET /access/{id}Update connection
PATCH /access/{id}Delete connection
DELETE /access/{id}Regenerate access key
POST /access/{id}/regenerate-key7. Data sync
Project sync status
GET /sync/status?project_id={project_id}List available connectors
GET /sync/connectorsReturns a list of all supported sync connector specs.
List sync jobs
GET /sync/syncs?project_id={project_id}Create sync
POST /sync/bootstrapRequest body:
{
"project_id": "proj_xxx",
"provider": "notion",
"config": {
"database_id": "xxx"
}
}Sync operations
POST /sync/syncs/{sync_id}/refresh # Manual refresh
POST /sync/syncs/{sync_id}/pause # Pause sync
POST /sync/syncs/{sync_id}/resume # Resume sync
PATCH /sync/syncs/{sync_id}/trigger # Update trigger mode
DELETE /sync/syncs/{sync_id} # Delete syncSync run history
GET /sync/syncs/{sync_id}/runs # List run records
GET /sync/runs/{run_id} # Run detailsFolder push and pull, for CLI sync
POST /sync/syncs/{sync_id}/push-file # Push local file
GET /sync/syncs/{sync_id}/pull-files # Pull cloud files
POST /sync/syncs/{sync_id}/ack-pull # Confirm pull completedSync changelog
GET /sync/changelog?project_id={project_id}8. Agent configuration
List agents
GET /agent-config/?project_id={project_id}Get agent
GET /agent-config/{agent_id}Get default agent
GET /agent-config/default?project_id={project_id}Create agent
POST /agent-config/Request body:
{
"name": "Support Assistant",
"project_id": "proj_xxx",
"system_prompt": "You are a professional support assistant...",
"model": "gpt-4o"
}Update agent
PUT /agent-config/{agent_id}Delete agent
DELETE /agent-config/{agent_id}Access control
POST /agent-config/{agent_id}/accesses # Add node access
PUT /agent-config/{agent_id}/accesses/{access_id} # Update access
DELETE /agent-config/{agent_id}/accesses/{access_id} # Remove access
PUT /agent-config/{agent_id}/accesses # Sync accesses in bulkBash tools
POST /agent-config/{agent_id}/bash # Add Bash tool
PUT /agent-config/{agent_id}/bash/{bash_id} # Update Bash tool
DELETE /agent-config/{agent_id}/bash/{bash_id} # Remove Bash tool
PUT /agent-config/{agent_id}/bash # Sync Bash tools in bulkExecution history
GET /agent-config/{agent_id}/executions9. Agent chat
SSE streaming chat
POST /agentsStream a conversation with an agent over Server-Sent Events, SSE.
Request body:
{
"agent_id": "agent_xxx",
"message": "Check the latest product data for me",
"session_id": "session_xxx"
}SSE event stream:
data: {"type": "text", "content": "Checking now..."}
data: {"type": "tool_call", "name": "query_data", "arguments": {...}}
data: {"type": "tool_result", "content": {...}}
data: {"type": "text", "content": "Here are the results..."}
data: {"type": "done"}Chat session management
POST /chat/sessions # Create session
GET /chat/sessions # List sessions
GET /chat/sessions/{session_id} # Get session
PATCH /chat/sessions/{session_id} # Update session
DELETE /chat/sessions/{session_id} # Delete session
GET /chat/sessions/{session_id}/messages # Get message history10. MCP endpoints
List MCP endpoints
GET /mcp-endpoints?project_id={project_id}Create MCP endpoint
POST /mcp-endpointsRequest body:
{
"name": "Product Data MCP",
"project_id": "proj_xxx",
"node_id": "node_xxx"
}Get MCP endpoint
GET /mcp-endpoints/{endpoint_id}Get MCP endpoint by node
GET /mcp-endpoints/by-node/{node_id}Update MCP endpoint
PUT /mcp-endpoints/{endpoint_id}Delete MCP endpoint
DELETE /mcp-endpoints/{endpoint_id}Regenerate API key
POST /mcp-endpoints/{endpoint_id}/regenerate-keyMCP tool bindings
GET /mcp/agents/{agent_id}/tools # List bound tools
POST /mcp/agents/{agent_id}/tools # Bind tool
PUT /mcp/agents/{agent_id}/tools/{tool_id} # Update binding
DELETE /mcp/agents/{agent_id}/tools/{tool_id} # Unbind tool
GET /mcp/agents/{agent_id}/status # MCP status
POST /mcp/agents/{agent_id}/regenerate-key # Regenerate MCP keyMCP proxy forwarding
ANY /mcp/proxy # MCP server proxy
ANY /mcp/proxy/{path} # MCP server proxy subpath11. Sandbox endpoints
List sandbox endpoints
GET /sandbox-endpoints?project_id={project_id}Create sandbox endpoint
POST /sandbox-endpointsRequest body:
{
"name": "Code Sandbox",
"project_id": "proj_xxx",
"node_id": "node_xxx"
}Get sandbox endpoint
GET /sandbox-endpoints/{endpoint_id}Get sandbox endpoint by node
GET /sandbox-endpoints/by-node/{node_id}Update sandbox endpoint
PUT /sandbox-endpoints/{endpoint_id}Delete sandbox endpoint
DELETE /sandbox-endpoints/{endpoint_id}Regenerate access key
POST /sandbox-endpoints/{endpoint_id}/regenerate-keyExecute command
POST /sandbox-endpoints/{endpoint_id}/execRequest body:
{
"command": "jq '.products | length' data.json"
}Response:
{
"stdout": "42",
"stderr": "",
"exit_code": 0
}12. Tools
List tools
GET /tools/?org_id={org_id}List tools by node
GET /tools/by-node/{node_id}List tools by project
GET /tools/by-project/{project_id}Create tool
POST /tools/Request body:
{
"name": "query_products",
"node_id": "node_xxx",
"type": "query",
"config": {}
}Create search tool
POST /tools/searchCreates a search tool with a vector index asynchronously.
Get search index status
GET /tools/{tool_id}/search-indexGet, update, or delete a tool
GET /tools/{tool_id}
PUT /tools/{tool_id}
DELETE /tools/{tool_id}13. Ingestion
Submit file ingestion
POST /ingest/submit/fileUpload PDFs, DOCX files, images, and more, then automatically run ETL into Markdown or JSON.
Submit URL or SaaS ingestion
POST /ingest/submit/saasPull data from a URL or SaaS platform.
Task management
GET /ingest/tasks/{task_id} # Get task status
POST /ingest/tasks/batch # Get task status in bulk
DELETE /ingest/tasks/{task_id} # Cancel task
GET /ingest/health # Ingestion service health checkETL rules
GET /ingest/rules # List ETL rules
POST /ingest/rules # Create ETL rule
GET /ingest/rules/{rule_id} # Get rule details
DELETE /ingest/rules/{rule_id} # Delete rule14. OAuth
PuppyOne supports OAuth integrations for multiple platforms. Each platform follows the same endpoint pattern:
GET /oauth/{provider}/authorize # Get authorization URL
POST /oauth/{provider}/callback # Handle callback
GET /oauth/{provider}/status # Check connection status
DELETE /oauth/{provider}/disconnect # DisconnectSupported {provider} values:
| Provider | Description |
|---|---|
notion | Notion workspace |
github | GitHub repository |
gmail | Gmail inbox |
google-drive | Google Drive files |
google-docs | Google Docs documents |
google-sheets | Google Sheets spreadsheets |
google-calendar | Google Calendar |
linear | Linear project management |
airtable | Airtable bases |
Example, start Notion OAuth:
curl "https://api.puppyone.ai/api/v1/oauth/notion/authorize?project_id=proj_xxx" \
-H "Authorization: Bearer <token>"Generic browser callback:
GET /oauth/callbackOther endpoints
Database connections
POST /db-connector/access # Create DB access
GET /db-connector/access # List access points
DELETE /db-connector/access/{id} # Delete access point
GET /db-connector/access/{id}/tables # List database tables
GET /db-connector/access/{id}/tables/{table}/preview # Preview table data
POST /db-connector/access/{id}/save # Save as content nodePublic publishing
POST /publishes/ # Create publish
GET /publishes/ # List publishes
PATCH /publishes/{publish_id} # Update publish
DELETE /publishes/{publish_id} # Delete publishPublic access, no auth required:
GET /p/{publish_key}Usage analytics
GET /analytics/access-timeseries # Access time series
GET /analytics/access-summary # Access summaryUser profile
GET /profile/me # Get current user info
GET /profile/onboarding/status # Onboarding status
POST /profile/onboarding/complete # Complete onboarding
POST /profile/onboarding/reset # Reset onboardingWorkspace
POST /workspace/create # Create workspace
POST /workspace/{agent_id}/complete # Complete agent workspace
GET /workspace/{agent_id}/status # Workspace statusError codes
| Error code | HTTP status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Invalid or missing token |
FORBIDDEN | 403 | Insufficient permissions |
NOT_FOUND | 404 | Resource not found |
VALIDATION_ERROR | 422 | Invalid request parameters |
RATE_LIMITED | 429 | Too many requests |
INTERNAL_ERROR | 500 | Internal server error |
Code examples
cURL: write a JSON file
curl -X POST "https://api.puppyone.ai/api/v1/content/proj_xxx/write" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"path": "products.json",
"node_type": "json",
"content": {
"products": [
{ "id": "SKU-001", "name": "Widget", "price": 59.99 }
]
},
"message": "Add product catalog"
}'Python: list and read content
import requests
API_BASE = "https://api.puppyone.ai/api/v1"
PROJECT_ID = "proj_xxx"
TOKEN = "<your-token>"
headers = {"Authorization": f"Bearer {TOKEN}"}
resp = requests.get(
f"{API_BASE}/content/{PROJECT_ID}/ls",
headers=headers,
).json()
for entry in resp["data"]:
print(f"{entry['name']} ({entry['type']})")JavaScript: get version history
const API_BASE = "https://api.puppyone.ai/api/v1";
const PROJECT_ID = "proj_xxx";
async function getVersionHistory(token, filePath) {
const params = new URLSearchParams({ path: filePath });
const response = await fetch(
`${API_BASE}/content/${PROJECT_ID}/versions?${params}`,
{
headers: { "Authorization": `Bearer ${token}` }
}
);
return response.json();
}