Projects API
HTTP API reference for creating, listing, updating, and deleting projects -- the second level of CodeSpar's account -> project tenancy model.
Projects API
The Projects API manages the second level of CodeSpar's tenancy model: Account -> Project. See the Projects concept for the full model.
Base URL: https://api.codespar.dev
All endpoints require authentication via Bearer token. See Authentication.
Project object
| Field | Type | Description |
|---|---|---|
id | string | Project ID in the form prj_<16chars> |
org_id | string | Parent account ID |
name | string | Display name (free-form) |
slug | string | URL-safe identifier, unique per account |
is_default | boolean | true for the account's default project (exactly one per account) |
created_at | string | ISO 8601 timestamp |
Slug rules
- Lowercase alphanumeric characters plus
_and- - Max length: 64 characters
defaultis reserved (only the auto-created default project uses it)- Must be unique within the account
Violations return slug_invalid, slug_reserved, or slug_conflict (see Errors).
GET /v1/projects
Lists all projects in the authenticated account.
Auth required: Yes
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
is_default | boolean | -- | Filter to only the default project (true) or non-default projects (false) |
limit | number | 50 | Results per page (max 100) |
offset | number | 0 | Pagination offset |
curl example
curl https://api.codespar.dev/v1/projects \
-H "Authorization: Bearer csk_live_abc123..."Response -- 200 OK
{
"data": [
{
"id": "prj_a1b2c3d4e5f6g7h8",
"org_id": "org_xyz789",
"name": "Default",
"slug": "default",
"is_default": true,
"created_at": "2026-04-01T10:00:00Z"
},
{
"id": "prj_i9j0k1l2m3n4o5p6",
"org_id": "org_xyz789",
"name": "Staging",
"slug": "staging",
"is_default": false,
"created_at": "2026-04-15T09:12:00Z"
}
],
"total": 2,
"limit": 50,
"offset": 0
}GET /v1/projects/:id
Retrieves a single project by ID.
Auth required: Yes
curl example
curl https://api.codespar.dev/v1/projects/prj_a1b2c3d4e5f6g7h8 \
-H "Authorization: Bearer csk_live_abc123..."Response -- 200 OK
{
"id": "prj_a1b2c3d4e5f6g7h8",
"org_id": "org_xyz789",
"name": "Default",
"slug": "default",
"is_default": true,
"created_at": "2026-04-01T10:00:00Z"
}POST /v1/projects
Creates a new project in the authenticated account.
Auth required: Yes
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name |
slug | string | Yes | URL-safe identifier (see slug rules) |
New projects are always created with is_default: false. To promote a project to default, use PATCH /v1/projects/:id with {"is_default": true}.
curl example
curl -X POST https://api.codespar.dev/v1/projects \
-H "Authorization: Bearer csk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"name": "Production",
"slug": "prod"
}'Response -- 201 Created
{
"id": "prj_q7r8s9t0u1v2w3x4",
"org_id": "org_xyz789",
"name": "Production",
"slug": "prod",
"is_default": false,
"created_at": "2026-04-20T14:22:00Z"
}PATCH /v1/projects/:id
Updates a project's name, slug, or default status.
Auth required: Yes
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | New display name |
slug | string | No | New slug (subject to slug rules) |
is_default | boolean | No | Set to true to promote this project to default. Atomic: the previous default is demoted in the same transaction. |
is_default can only be set to true. You cannot un-set it directly -- to change the default, promote a different project instead. Sending {"is_default": false} on the current default project is a no-op or error (exact behavior: TODO -- confirm).
curl example -- promote to default
curl -X PATCH https://api.codespar.dev/v1/projects/prj_q7r8s9t0u1v2w3x4 \
-H "Authorization: Bearer csk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{"is_default": true}'Response -- 200 OK
{
"id": "prj_q7r8s9t0u1v2w3x4",
"org_id": "org_xyz789",
"name": "Production",
"slug": "prod",
"is_default": true,
"created_at": "2026-04-20T14:22:00Z"
}DELETE /v1/projects/:id
Deletes a project and all of its scoped resources (API keys, triggers, session history, connection records).
Auth required: Yes
Deletion cascades. There is no undo. Export audit logs first if you need to retain them.
curl example
curl -X DELETE https://api.codespar.dev/v1/projects/prj_q7r8s9t0u1v2w3x4 \
-H "Authorization: Bearer csk_live_abc123..."Response -- 200 OK
{
"id": "prj_q7r8s9t0u1v2w3x4",
"deleted": true
}Deletion fails with cannot_delete_default or cannot_delete_last_project (see below).
Project members (RBAC overrides)
Every user with account membership inherits a base role (owner, admin, member) that applies across every project in that account. The four endpoints below let an account admin narrow or widen that role for a specific project — useful when a contractor should see exactly one project, or a member needs admin rights on staging without becoming a full account admin.
A user absent from project_members inherits their account-level role unchanged. Explicit rows are overrides, not the source of truth.
Not reachable with an API key — use the dashboard
The four member endpoints live in the API's service-auth subtree
(requireServiceAuth), alongside api-keys, usage, billing and team. A csk_
Bearer key is not read at all there, so a Bearer call answers
401 {"error":"unauthorized"} — not a 403 and not a 404.
There is no key we can issue that changes this: the credential for that subtree is CodeSpar's own platform secret, and possession of it is the trust boundary for every account, so it never leaves our infrastructure. The dashboard is the supported surface. The request and response shapes below describe what it sends and renders.
GET /v1/projects/:id/members
Lists every user with effective access to the project, including account-level inherited members. The source field tells you whether the role came from a project override or from the account membership.
GET /v1/projects/prj_a1b2c3d4e5f6g7h8/membersResponse — 200 OK
{
"members": [
{
"user_id": "user_owner123",
"role": "owner",
"source": "org",
"email": "founder@codespar.dev",
"display_name": "Founder",
"avatar_url": null,
"added_at": "2026-04-15T10:00:00Z"
},
{
"user_id": "user_contractor456",
"role": "admin",
"source": "project",
"email": "ana@contractor.com",
"display_name": "Ana",
"avatar_url": null,
"added_at": "2026-04-22T18:30:00Z"
}
]
}Sorted: owner first, then admin, then member — oldest first within each tier.
POST /v1/projects/:id/members
Adds or upserts a project-level override. Requires account-level admin or owner.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
user_id | string | Yes | Must already be a member of the account. |
role | admin | member | Yes | Cannot override an account owner — they always retain full access. |
POST /v1/projects/prj_.../members
Content-Type: application/json
{"user_id": "user_contractor456", "role": "admin"}PATCH /v1/projects/:id/members/:user_id
Updates an existing override's role. Same gate (admin+ at account level).
PATCH /v1/projects/prj_.../members/user_contractor456
Content-Type: application/json
{"role": "member"}DELETE /v1/projects/:id/members/:user_id
Removes the override. The user falls back to their account-level role for this project.
DELETE /v1/projects/prj_.../members/user_contractor456Member-related error codes
| Status | Error code | When |
|---|---|---|
401 | unauthorized | Called with a Bearer key, or with no/invalid service key. Bare-string shape |
403 | insufficient_role | POST/PATCH/DELETE by a non-admin account member. Flat shape: {"error":"insufficient_role","required":"admin"} |
404 | not_found | The project id does not exist in this account, or a PATCH named a user with no override row |
404 | user_not_in_org | user_id is not a member of this account |
409 | cannot_override_owner | The target user is the account owner; an owner's role cannot be narrowed per project |
DELETE is idempotent: removing an override that does not exist answers 204,
not 404 — the end state is the same, the user inherits their account role.
Errors
All endpoints follow the standard error format:
{
"error": "error_code",
"message": "Human-readable error description.",
"status": 400
}Project-specific error codes
| Status | Error code | When |
|---|---|---|
400 | slug_invalid | Slug contains disallowed characters, exceeds 64 chars, or is empty |
400 | slug_reserved | Slug is default (reserved for the auto-created default project) |
409 | slug_conflict | Another project in the account already uses this slug |
409 | cannot_delete_default | DELETE on the default project. Promote another project first. |
409 | cannot_delete_last_project | DELETE on the only remaining project. Every account must have at least one project. |
Standard error codes
| Status | Error code | When |
|---|---|---|
401 | unauthorized | Invalid or missing API key |
403 | forbidden | API key lacks permission (e.g. a project-scoped key trying to manage a different project) |
404 | not_found | Project ID does not exist or does not belong to the authenticated account |
429 | rate_limited | Too many requests |
500 | internal_error | Server error. Retry with exponential backoff. |
Next steps
Servers API
HTTP API reference for browsing and filtering CodeSpar's catalog of MCP servers across payments, fiscal, logistics, messaging, banking, ERP, and crypto.
@codespar/api-types
Zod schemas + TypeScript types for every /v1/* REST shape on api.codespar.dev. Used internally by the SDK and dashboard; published for callers building HTTP clients directly.