Internal Documentation internal
TalkIDE internal documentation

Update an existing project’s name, description, URL, accent color, and tech stack. Requires a valid JWT access token. The project must belong to the authenticated user’s tenant and must not be in ARCHIVED status.

  • This is a full replace operation — all fields must be sent in the request body. name is required (not blank); optional fields (description, url, accent, techStack) can be set to null to clear their value.
  • If the URL is changed, the new URL must be globally unique (same rules as during creation).
  • The project status cannot be changed through this endpoint — use dedicated status-change endpoints (Archive, Restore).
  • Projects in ARCHIVED status cannot be updated — the user must restore the project first.
sequenceDiagram
    actor User

    User->>+FE: edits project fields

    FE->>FE: validate form
    alt form is invalid
        FE-->>User: show error messages <br> disable submit button
    end

    User->>+FE: submits update form

    FE->>+BE: PUT /api/v1/projects/{id} <br> Authorization: Bearer {accessToken} <br> UpdateProjectRequest

    BE->>BE: validate JWT access token
    alt access token invalid or missing
        BE-->>FE: 401 Unauthorized <br> ErrorResponse
    end

    BE->>BE: validate request
    alt request is invalid
        BE-->>FE: 400 Bad Request <br> ErrorResponse
    end

    BE->>DB: find project by id and tenant_id
    alt project not found
        BE-->>FE: 404 Not Found <br> ErrorResponse
    end

    BE->>BE: check project status
    alt project is ARCHIVED
        BE-->>FE: 409 Conflict <br> ErrorResponse
    end

    BE->>DB: check if new URL is already taken (if URL changed)
    alt URL already taken
        BE-->>FE: 409 Conflict <br> ErrorResponse
    end

    BE->>DB: update project record

    BE->>-FE: 200 OK <br> ProjectResponse

    FE->>-User: show updated project

    FE->>-User: show success notification

PUT /api/v1/projects/{id} UpdateProjectRequest:

{
  "name": "Wildwood Bakery & Cafe",
  "description": "Online ordering for a neighborhood bakery and cafe",
  "url": "wildwood-cafe.talkide.app",
  "accent": "oklch(0.80 0.14 65)",
  "techStack": "Vue + Spring Boot"
}

200 OK ProjectResponse:

{
  "data": {
    "id": 1,
    "name": "Wildwood Bakery & Cafe",
    "description": "Online ordering for a neighborhood bakery and cafe",
    "status": "DRAFT",
    "url": "wildwood-cafe.talkide.app",
    "accent": "oklch(0.80 0.14 65)",
    "techStack": "Vue + Spring Boot",
    "progress": null,
    "createdAt": "2026-04-20T10:00:00Z",
    "updatedAt": "2026-04-29T15:00:00Z"
  }
}

400 Bad Request (validation) ErrorResponse:

{
  "status": 400,
  "code": "VALIDATION_ERROR",
  "message": "Validation failed",
  "errors": [
    { "field": "name", "message": "must not be blank" }
  ]
}

401 Unauthorized (missing or invalid access token) ErrorResponse:

{
  "status": 401,
  "code": "AUTHENTICATION_FAILED",
  "message": "Access token is missing or invalid"
}

404 Not Found (project does not exist or does not belong to user’s tenant) ErrorResponse:

{
  "status": 404,
  "code": "NOT_FOUND",
  "message": "Project not found"
}

409 Conflict (project is ARCHIVED) ErrorResponse:

{
  "status": 409,
  "code": "CONFLICT_PROJECT",
  "message": "Cannot update an archived project"
}

409 Conflict (URL already taken) ErrorResponse:

{
  "status": 409,
  "code": "CONFLICT_PROJECT",
  "message": "URL is already taken by another project"
}

Frontend

Validations

FieldConstraintsSizePatternNote
namenot_blank1 - 100
description(optional)0 - 500
url(optional)0 - 253^[a-z0-9-]+\.talkide\.app$
accent(optional)0 - 100
techStack(optional)0 - 100

Backend

Validations

FieldConstraintsSizePatternNote
namenot_blank1 - 100
description(optional)0 - 500
url(optional)0 - 253^[a-z0-9-]+\.talkide\.app$Must be globally unique if changed
accent(optional)0 - 100
techStack(optional)0 - 100

Test Cases

GIVENWHENTHEN
authenticated user, project in DRAFT status, valid requestPUT /projects/{id} is called200 OK with updated project returned
authenticated user, project in LIVE status, valid requestPUT /projects/{id} is called200 OK with updated project returned
authenticated user, project in UPDATED status, valid requestPUT /projects/{id} is called200 OK with updated project returned
authenticated user, project in BUILDING status, valid requestPUT /projects/{id} is called200 OK with updated project returned
authenticated user, project in PAUSED status, valid requestPUT /projects/{id} is called200 OK with updated project returned
authenticated user, project in ARCHIVED statusPUT /projects/{id} is called409 CONFLICT_PROJECT error response is returned
authenticated user, URL changed to an already taken URLPUT /projects/{id} is called409 CONFLICT_PROJECT error response is returned
authenticated user, URL unchanged (same as current)PUT /projects/{id} is called200 OK with updated project returned
project belongs to another tenantPUT /projects/{id} is called404 NOT_FOUND error response is returned
project ID does not existPUT /projects/{id} is called404 NOT_FOUND error response is returned
name is blankPUT /projects/{id} is called400 VALIDATION_ERROR error response is returned
name longer than 100 charactersPUT /projects/{id} is called400 VALIDATION_ERROR error response is returned
URL does not end with .talkide.appPUT /projects/{id} is called400 VALIDATION_ERROR error response is returned
invalid request (empty body)PUT /projects/{id} is called400 VALIDATION_ERROR error response is returned
no Authorization headerPUT /projects/{id} is called401 AUTHENTICATION_FAILED error response is returned

Was this page helpful?

Thanks for the feedback.