Projects Api V1
Djass Projects API (v1)
Base URL: https://<your-djass-host>/api/v1
Coverage: create, list, get, status endpoints for project generation.
1) What this API does
This API lets an agent: 1. create a new Djass project generation job, 2. list the caller's projects, 3. fetch one project by id, 4. poll status until generation is complete.
All data access is owner-scoped: keys can only see projects for their own profile.
2) Authentication and key scoping
All endpoints require an API key.
Accepted auth formats (in lookup order)
X-API-Key: <key>Authorization: Bearer <key>(also acceptsApi-Key,apikey,tokenschemes)- Query fallback:
?api_key=<key>(legacy compatibility)
Key types
- Legacy profile key: full access to
projects:create+projects:read - Scoped API key (
ProjectAPIKey): only granted scopes
Required scopes
POST /projectsrequiresprojects:createGET /projects,GET /projects/{id},GET /projects/{id}/statusrequireprojects:read
If scope is missing: 403 with error.code = "insufficient_scope".
3) Error contract (all non-2xx)
{
"error": {
"code": "machine_readable_code",
"category": "validation|auth|quota|retryable|internal",
"message": "Human readable summary",
"retryable": false,
"details": {}
}
}
Common codes used by this API
auth_requiredinsufficient_scopesubscription_requiredinvalid_project_slugquota_exceededproject_not_foundretryable_errorinternal_error
Retry guidance may appear in error.details.retry_guidance.
4) Endpoint reference
4.1 Create project
Method/Path: POST /projects
Purpose: queue project generation from template settings.
Request body
{
"project_name": "Acme CRM",
"project_slug": "acme_crm",
"project_description": "Internal CRM for support and sales",
"repo_url": "https://github.com/acme/acme-crm",
"author_name": "Acme Bot",
"author_email": "[email protected]",
"author_url": "https://acme.test",
"project_main_color": "green",
"use_posthog": "y",
"use_buttondown": "y",
"use_s3": "y",
"use_stripe": "y",
"use_sentry": "y",
"generate_blog": "y",
"generate_docs": "y",
"use_mjml": "y",
"use_ai": "y",
"use_logfire": "y",
"use_healthchecks": "y",
"use_ci": "y"
}
Notes:
- all feature flags are "y"|"n"
- project_slug is normalized (slugify, then - -> _)
- if author_email is empty, backend fills it from profile email
Success response (201)
{
"project": {
"id": 123,
"name": "Acme CRM",
"slug": "acme_crm",
"status": "queued",
"error_message": "",
"created_at": "2026-03-11T12:00:00Z",
"updated_at": "2026-03-11T12:00:00Z",
"started_at": null,
"finished_at": null,
"artifact_ready": false,
"input_payload": {
"project_name": "Acme CRM",
"project_slug": "acme_crm",
"project_description": "Internal CRM for support and sales",
"repo_url": "https://github.com/acme/acme-crm",
"author_name": "Acme Bot",
"author_email": "[email protected]",
"author_url": "https://acme.test",
"project_main_color": "green",
"use_posthog": "y",
"use_buttondown": "y",
"use_s3": "y",
"use_stripe": "y",
"use_sentry": "y",
"generate_blog": "y",
"generate_docs": "y",
"use_mjml": "y",
"use_ai": "y",
"use_logfire": "y",
"use_healthchecks": "y",
"use_ci": "y"
}
}
}
Error statuses
400invalid payload / invalid slug401missing or invalid key403subscription required or insufficient scope429project quota exceeded503temporary queueing failure (retryable)500internal error
4.2 List projects
Method/Path: GET /projects
Purpose: list caller-owned projects with pagination and optional status filter.
Query params
limit(default20, min1, max100)offset(default0, min0)status(queued|generating|ready|failed)
Success response (200)
{
"projects": [
{
"id": 123,
"name": "Acme CRM",
"slug": "acme_crm",
"status": "ready",
"error_message": "",
"created_at": "2026-03-11T12:00:00Z",
"updated_at": "2026-03-11T12:02:40Z",
"started_at": "2026-03-11T12:00:05Z",
"finished_at": "2026-03-11T12:02:39Z",
"artifact_ready": true,
"input_payload": {
"project_name": "Acme CRM",
"project_slug": "acme_crm"
}
}
],
"total": 1,
"limit": 20,
"offset": 0,
"has_next": false,
"filters": {
"status": "ready"
}
}
Error statuses
401missing or invalid key403insufficient scope422invalid query params (limit/offset/status)500internal error
4.3 Get project
Method/Path: GET /projects/{project_id}
Purpose: fetch one full project object.
Success response (200)
Returns full Project object (same shape as project in create response).
Error statuses
401missing/invalid key403insufficient scope404project not found500internal error
4.4 Get project status
Method/Path: GET /projects/{project_id}/status
Purpose: lightweight polling endpoint.
Success response (200)
{
"id": 123,
"status": "generating",
"error_message": "",
"artifact_ready": false,
"started_at": "2026-03-11T12:00:05Z",
"finished_at": null,
"updated_at": "2026-03-11T12:00:20Z"
}
Error statuses
401missing/invalid key403insufficient scope404project not found500internal error
5) Agent quickstart (copy-paste)
# 0) Set runtime values
export DJASS_BASE_URL="https://your-djass-host/api/v1"
export DJASS_API_KEY="replace-with-key"
# 1) Create project
curl -sS -X POST "$DJASS_BASE_URL/projects" \
-H "Content-Type: application/json" \
-H "X-API-Key: $DJASS_API_KEY" \
--data '{
"project_name": "Acme CRM",
"project_slug": "acme_crm",
"project_description": "Internal CRM for support and sales",
"repo_url": "https://github.com/acme/acme-crm",
"author_name": "Acme Bot",
"author_email": "[email protected]",
"author_url": "https://acme.test",
"project_main_color": "green",
"use_posthog": "y",
"use_buttondown": "y",
"use_s3": "y",
"use_stripe": "y",
"use_sentry": "y",
"generate_blog": "y",
"generate_docs": "y",
"use_mjml": "y",
"use_ai": "y",
"use_logfire": "y",
"use_healthchecks": "y",
"use_ci": "y"
}'
# 2) Get latest project (or parse ID from create response)
curl -sS "$DJASS_BASE_URL/projects?limit=1&offset=0" \
-H "X-API-Key: $DJASS_API_KEY"
# 3) Poll status until ready/failed
PROJECT_ID="123"
curl -sS "$DJASS_BASE_URL/projects/$PROJECT_ID/status" \
-H "X-API-Key: $DJASS_API_KEY"
# 4) Fetch full project object
curl -sS "$DJASS_BASE_URL/projects/$PROJECT_ID" \
-H "X-API-Key: $DJASS_API_KEY"
Polling recommendation:
- start at 2s interval
- back off to max 15s
- stop on status in {"ready", "failed"}
6) Common failure cases (with real payload shapes)
Missing/invalid auth (401)
{
"error": {
"code": "auth_required",
"category": "auth",
"message": "Authentication required.",
"retryable": false,
"details": {}
}
}
Missing scope (403)
{
"error": {
"code": "insufficient_scope",
"category": "auth",
"message": "API key is missing required scope: projects:create",
"retryable": false,
"details": {
"required_scope": "projects:create"
}
}
}
Invalid slug (400)
{
"error": {
"code": "invalid_project_slug",
"category": "validation",
"message": "project_slug must contain letters or numbers.",
"retryable": false,
"details": {
"field": "project_slug"
}
}
}
Quota exceeded (429)
{
"error": {
"code": "quota_exceeded",
"category": "quota",
"message": "Project quota exceeded for this API identity.",
"retryable": false,
"details": {
"quota": 200,
"retry_guidance": "Delete old projects or request limit bump."
}
}
}
Temporary queue failure (503, retryable)
{
"error": {
"code": "retryable_error",
"category": "retryable",
"message": "Temporary failure while queueing project generation.",
"retryable": true,
"details": {
"retry_guidance": "Retry with exponential backoff."
}
}
}
Not found (404)
{
"error": {
"code": "project_not_found",
"category": "validation",
"message": "Project not found.",
"retryable": false,
"details": {
"project_id": 999999
}
}
}
7) Implementation references
This API reference is aligned with backend implementation and tests:
- endpoint logic: apps/api/views.py
- schema definitions: apps/api/schemas.py
- auth parsing/scoping: apps/api/auth.py, apps/api/utils.py
- API tests: apps/api/test_spec_001_contract.py
Local execution note:
- running API tests in this environment requires PostgreSQL with pgvector migration support (CREATE EXTENSION vector)
- sqlite fallback test run fails by design on that migration
8) Known limitations / TODO
- No idempotency key on create: repeated
POST /projectscan create duplicates. - No callback/webhook for completion: clients must poll
/status. - No artifact download endpoint in this spec: clients need another API/surface to fetch ZIP output.
- Legacy query-string auth remains enabled for compatibility; should eventually be deprecated for security hygiene.
- No server-provided request ID in response body for cross-system tracing.