Hosting Projects
Hosting projects represent deployed applications with isolated environments, app counts, and lifecycle management.
Endpoint
| Method | Path | Auth | Idempotent |
|---|
| GET | /api/projects
| Bearer | yes |
| POST | /api/projects
| Bearer | no |
| GET | /api/projects/:identifier
| Bearer | yes |
| PATCH | /api/projects/:id
| Bearer | no |
| DELETE | /api/projects/:id
| Bearer | yes |
Parameters
| Name | Type | Required | Default | Notes |
|---|
name
| string | yes | — | Human-readable project name |
slug
| string | no | auto-generated from name
| URL-safe identifier; generated via slugify() (lowercased, non-alphanumeric collapsed to hyphens, max 255 chars) |
identifier
| string | yes (path) | — | Resolved against both id (UUID) and slug ; the first match wins |
id
| string | yes (path for PATCH/DELETE) | — | Project UUID |
Response
// GET /api/projects — list all projects with enriched app count
{
projects: Array<{
id: string;
name: string;
slug: string;
// ... other hosting project fields ...
appCount: number; // count of apps belonging to this project
}>;
}
// GET /api/projects/:identifier — single project object
{
id: string;
name: string;
slug: string;
// ... other hosting project fields ...
}
// POST /api/projects (201) — created project object
{
id: string;
name: string;
slug: string;
// ... other hosting project fields ...
}
// PATCH /api/projects/:id — updated project object
{
id: string;
name: string;
slug: string;
// ... other hosting project fields ...
}
// DELETE /api/projects/:id — soft-delete confirmation
{ success: true }
// Error envelope (all routes)
{ error: { code: string; message: string } }
Examples
# List all hosting projects
curl -H "Authorization: Bearer $BF_API_KEY" \
https://api.blueforge.studio/api/projects
# Create a hosting project
curl -X POST https://api.blueforge.studio/api/projects \
-H "Authorization: Bearer $BF_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "My App", "slug": "my-app"}'
# Get project by slug
curl -H "Authorization: Bearer $BF_API_KEY" \
https://api.blueforge.studio/api/projects/my-app
# Update project metadata
curl -X PATCH https://api.blueforge.studio/api/projects/prj_xxx \
-H "Authorization: Bearer $BF_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "My App v2"}'
# Soft-delete a project
curl -X DELETE https://api.blueforge.studio/api/projects/prj_xxx \
-H "Authorization: Bearer $BF_API_KEY"
import { Client } from "@blueforge/client"
const client = new Client({ apiKey: process.env.BF_API_KEY })
// List projects
const { projects } = await client.get("/api/projects")
console.log(projects.map(p => `${p.name} (${p.appCount} apps)`))
// Create project
const created = await client.post("/api/projects", { name: "My App" })
// Get by slug
const found = await client.get(`/api/projects/my-app`)
// Update
const updated = await client.patch(`/api/projects/${created.id}`, { name: "Renamed" })
// Delete
await client.delete(`/api/projects/${created.id}`)
Pitfalls
- The
identifier
path param resolves against both id
and slug
in parallel. If a project's slug happens to collide with another project's UUID prefix, either may match first. Use UUIDs for exact lookups.
- DELETE is a soft delete. The row is marked as deleted in the database but not physically removed.
- The
slug
is capped at 255 characters after slugification. Very long names are silently truncated.
- The list endpoint enriches each project with
appCount
via a per-project COUNT query — performance degrades linearly with the number of projects.
See also
Tested against
- @blueforge/hosting-api: 2.4.1
- Last verified: 2026-07-15