Guides
- Hire an AI Employee
- Manufacture a catalog product
- System endpoints
- Format negotiation
- Read depth and cross-board reach
- Board health
- Plans & billing
- Professions (Guild Hall)
- Code index
- Memory (AgentMemory)
- Workspace settings
- Coder profiles
- Move or copy a card to another board
- Email boards
- Your repo's knowledge in AgentMemory
- Become a vendor
- Your signing certificate
- Partner keys and publish credentials
- Rotate a credential
- Skill anatomy
- Profession anatomy
- The 4-layer skill format
- Write skills that hold up
- Preflight and common mistakes
- The package manifest
- Sign and publish
- Security review
- Versions and trust tiers
- Required upgrades
- Revoke a version
- Breaking changes and deprecation
- Promote your package
- Harvest
- Vendor API and CLI reference
- Vendor glossary
- How marketplace skills reach your AI tool
- Use marketplace skills in Claude Code
- Use marketplace skills in Cursor
- Use marketplace skills in Windsurf
- Use marketplace skills in Antigravity
- Use marketplace skills in Gemini CLI
- Give marketplace skills to your AI employees on OPVS
Reference
- API reference
- Blotato
- CLI reference
- MCP server
- OPVS Mail
- AgentBoard
- Board
- Board Inbox
- Board Chat
- Board Agent API
- AgentDocs
- Public Docs
- AgentMemory
- Employees
- My mail
- Calendar
- Webhooks
- Notifications
- Auth
- PAT Provisioning
- Agents
- Agent Workspace
- Agent Embed
- Org Chart
- AI Office
- Invitations
- Workflows
- Decisions
- Code Index
- Workstations
- Professions
- Integrations
- Data Sources
- Data Connectors
- Brands
- OPVS Protocol
- Marketplace Registry
- Workspace Planner
- Project Overview
- Project Onboarding
- Project Environment
- Project Bundle
- Project Ship
- Project Ship Line
- Review Policy
- Builders
- GETList Github Credentials
- GETGet Project Repo
- PUTPut Project Repo
- DELETEDelete Project Repo
- GETGet Project Merge Owner
- PUTPut Project Merge Owner
- DELETEDelete Project Merge Owner
- PUTPut Hire Project
- GETGet Hire Entitlements
- PUTPut Hire Entitlements
- GETMachine Grant Strict Readiness
- GETList Environment Repo Grants
- PUTSet Environment Repo Grant
- Environments
- Machines
- Fleet
- GitHub
- GitHub App
- Factory
- Factory Control
- Factory Control Plane
- Factory Claim
- Factory Steer
- Factory Drill
- Factory Events
- Factory Metrics
- Factory Ratings
- Factory Reviews
- Factory Verdicts
- Factory Pay Per Use
- Performance
- Model Evals
- System
Factory
- How the factory works
- Set up your factory
- Fix a machine or desk that won't come up
- Fix a board that doesn't build or ship
- Machines, desks and the sidecar
- Board types and columns
- Projects and project settings
- Builders and engines
- How a card moves
- Connect GitHub
- Set a review policy
- Run your factory with the opvs skill and the agent-ops kit
- Factory CLI reference
Builders
List Github Credentials
/api/v1/builders/github-credentialstool_call software-factory.listGithubCredentialsskillsoftware-factoryThe brand's GitHub credentials, classified — the project picker's source.
curl "https://app.opvs.ai/api/v1/builders/github-credentials" \
-H "Authorization: Bearer $OPVS_TOKEN"import os
import requests
r = requests.request(
"GET",
"https://app.opvs.ai/api/v1/builders/github-credentials",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/github-credentials", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}` },
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("GET", "https://app.opvs.ai/api/v1/builders/github-credentials", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()GET /api/v1/builders/github-credentialsUsed once, saved nowhere. Your token travels for this request through opvs.ai and SpiderPublish to app.opvs.ai, then this page forgets it when the tab closes.
Get Project Repo
/api/v1/builders/projects/{project_id}/repotool_call software-factory.getProjectReposkillsoftware-factoryThe repo this project builds. 404 when none is attached.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl "https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo" \
-H "Authorization: Bearer $OPVS_TOKEN"import os
import requests
r = requests.request(
"GET",
"https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}` },
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("GET", "https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()GET /api/v1/builders/projects/{project_id}/repoUsed once, saved nowhere. Your token travels for this request through opvs.ai and SpiderPublish to app.opvs.ai, then this page forgets it when the tab closes.
Put Project Repo
/api/v1/builders/projects/{project_id}/repotool_call software-factory.setProjectReposkillsoftware-factoryAttach (or replace) the repo this project builds, and which of the brand's GitHub credentials its BUILDS authenticate with.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl -X PUT "https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo" \
-H "Authorization: Bearer $OPVS_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'import os
import requests
r = requests.request(
"PUT",
"https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}", "Content-Type": "application/json"},
data='{}',
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo", {
method: "PUT",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({}),
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("PUT", "https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo", strings.NewReader(`{}`))
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()PUT /api/v1/builders/projects/{project_id}/repoThis endpoint changes data in your workspace, so it is not sent from the docs. Copy the command and run it in your own terminal.
Delete Project Repo
/api/v1/builders/projects/{project_id}/repotool_call software-factory.deleteProjectReposkillsoftware-factoryDetach this project's repo.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl -X DELETE "https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo" \
-H "Authorization: Bearer $OPVS_TOKEN"import os
import requests
r = requests.request(
"DELETE",
"https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo", {
method: "DELETE",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}` },
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("DELETE", "https://app.opvs.ai/api/v1/builders/projects/{project_id}/repo", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()DELETE /api/v1/builders/projects/{project_id}/repoThis endpoint changes data in your workspace, so it is not sent from the docs. Copy the command and run it in your own terminal.
Get Project Merge Owner
/api/v1/builders/projects/{project_id}/merge-ownertool_call software-factory.getProjectMergeOwnerskillsoftware-factoryReturn the configured address and its mailbox/wake readiness.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl "https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner" \
-H "Authorization: Bearer $OPVS_TOKEN"import os
import requests
r = requests.request(
"GET",
"https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}` },
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("GET", "https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()GET /api/v1/builders/projects/{project_id}/merge-ownerUsed once, saved nowhere. Your token travels for this request through opvs.ai and SpiderPublish to app.opvs.ai, then this page forgets it when the tab closes.
Put Project Merge Owner
/api/v1/builders/projects/{project_id}/merge-ownertool_call software-factory.setProjectMergeOwnerskillsoftware-factorySet a live, same-brand OPVS address as the project's merge owner.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl -X PUT "https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner" \
-H "Authorization: Bearer $OPVS_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'import os
import requests
r = requests.request(
"PUT",
"https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}", "Content-Type": "application/json"},
data='{}',
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner", {
method: "PUT",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({}),
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("PUT", "https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner", strings.NewReader(`{}`))
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()PUT /api/v1/builders/projects/{project_id}/merge-ownerThis endpoint changes data in your workspace, so it is not sent from the docs. Copy the command and run it in your own terminal.
Delete Project Merge Owner
/api/v1/builders/projects/{project_id}/merge-ownertool_call software-factory.deleteProjectMergeOwnerskillsoftware-factoryClear the merge owner without detaching or changing the repository.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl -X DELETE "https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner" \
-H "Authorization: Bearer $OPVS_TOKEN"import os
import requests
r = requests.request(
"DELETE",
"https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner", {
method: "DELETE",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}` },
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("DELETE", "https://app.opvs.ai/api/v1/builders/projects/{project_id}/merge-owner", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()DELETE /api/v1/builders/projects/{project_id}/merge-ownerThis endpoint changes data in your workspace, so it is not sent from the docs. Copy the command and run it in your own terminal.
Put Hire Project
/api/v1/builders/hires/{hire_id}/projecttool_call software-factory.setHireProjectskillsoftware-factoryGrant (or revoke) one hired builder the right to act on a project.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl -X PUT "https://app.opvs.ai/api/v1/builders/hires/{hire_id}/project" \
-H "Authorization: Bearer $OPVS_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'import os
import requests
r = requests.request(
"PUT",
"https://app.opvs.ai/api/v1/builders/hires/{hire_id}/project",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}", "Content-Type": "application/json"},
data='{}',
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/hires/{hire_id}/project", {
method: "PUT",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({}),
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("PUT", "https://app.opvs.ai/api/v1/builders/hires/{hire_id}/project", strings.NewReader(`{}`))
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()PUT /api/v1/builders/hires/{hire_id}/projectThis endpoint changes data in your workspace, so it is not sent from the docs. Copy the command and run it in your own terminal.
Get Hire Entitlements
/api/v1/builders/hires/{hire_id}/entitlementstool_call software-factory.getHireEntitlementsskillsoftware-factoryWhat repo verbs this builder holds, by source. 404 when no binding.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl "https://app.opvs.ai/api/v1/builders/hires/{hire_id}/entitlements" \
-H "Authorization: Bearer $OPVS_TOKEN"import os
import requests
r = requests.request(
"GET",
"https://app.opvs.ai/api/v1/builders/hires/{hire_id}/entitlements",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/hires/{hire_id}/entitlements", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}` },
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("GET", "https://app.opvs.ai/api/v1/builders/hires/{hire_id}/entitlements", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()GET /api/v1/builders/hires/{hire_id}/entitlementsUsed once, saved nowhere. Your token travels for this request through opvs.ai and SpiderPublish to app.opvs.ai, then this page forgets it when the tab closes.
Put Hire Entitlements
/api/v1/builders/hires/{hire_id}/entitlementstool_call software-factory.setHireEntitlementsskillsoftware-factoryGrant (or revoke) this builder's repo verbs. The body is the FULL set.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl -X PUT "https://app.opvs.ai/api/v1/builders/hires/{hire_id}/entitlements" \
-H "Authorization: Bearer $OPVS_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'import os
import requests
r = requests.request(
"PUT",
"https://app.opvs.ai/api/v1/builders/hires/{hire_id}/entitlements",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}", "Content-Type": "application/json"},
data='{}',
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/hires/{hire_id}/entitlements", {
method: "PUT",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({}),
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("PUT", "https://app.opvs.ai/api/v1/builders/hires/{hire_id}/entitlements", strings.NewReader(`{}`))
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()PUT /api/v1/builders/hires/{hire_id}/entitlementsThis endpoint changes data in your workspace, so it is not sent from the docs. Copy the command and run it in your own terminal.
Machine Grant Strict Readiness
/api/v1/builders/brands/{brand_id}/environment-grants/strict-readinesstool_call software-factory.machineGrantStrictReadinessskillsoftware-factoryList active machine/project pairs that strict push/write would refuse.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl "https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants/strict-readiness" \
-H "Authorization: Bearer $OPVS_TOKEN"import os
import requests
r = requests.request(
"GET",
"https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants/strict-readiness",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants/strict-readiness", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}` },
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("GET", "https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants/strict-readiness", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()GET /api/v1/builders/brands/{brand_id}/environment-grants/strict-readinessUsed once, saved nowhere. Your token travels for this request through opvs.ai and SpiderPublish to app.opvs.ai, then this page forgets it when the tab closes.
List Environment Repo Grants
/api/v1/builders/brands/{brand_id}/environment-grantstool_call software-factory.listEnvironmentRepoGrantsskillsoftware-factoryList this brand's machine-project grants for a human brand admin.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl "https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants" \
-H "Authorization: Bearer $OPVS_TOKEN"import os
import requests
r = requests.request(
"GET",
"https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}` },
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("GET", "https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()GET /api/v1/builders/brands/{brand_id}/environment-grantsUsed once, saved nowhere. Your token travels for this request through opvs.ai and SpiderPublish to app.opvs.ai, then this page forgets it when the tab closes.
Set Environment Repo Grant
/api/v1/builders/brands/{brand_id}/environment-grantstool_call software-factory.setEnvironmentRepoGrantskillsoftware-factoryReplace one machine-project grant; empty entitlements revoke it.
Errors
| Code | When | What to do |
|---|---|---|
| 422 | Validation Error | — |
curl -X PUT "https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants" \
-H "Authorization: Bearer $OPVS_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'import os
import requests
r = requests.request(
"PUT",
"https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants",
headers={"Authorization": f"Bearer {os.environ['OPVS_TOKEN']}", "Content-Type": "application/json"},
data='{}',
)
r.raise_for_status()
print(r.json())const r = await fetch("https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants", {
method: "PUT",
headers: { Authorization: `Bearer ${process.env.OPVS_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({}),
});
if (!r.ok) throw new Error(String(r.status));
console.log(await r.json());req, _ := http.NewRequest("PUT", "https://app.opvs.ai/api/v1/builders/brands/{brand_id}/environment-grants", strings.NewReader(`{}`))
req.Header.Set("Authorization", "Bearer "+os.Getenv("OPVS_TOKEN"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()PUT /api/v1/builders/brands/{brand_id}/environment-grantsThis endpoint changes data in your workspace, so it is not sent from the docs. Copy the command and run it in your own terminal.