ProjectsUpdate a project

Update a project

Updates a project's header fields (name, customer, people, type, dates, budgets).

MIXED replace / patch semantics: Name, CustomerID, ContactID, Description and ProjectManagerID are always written; the nullable fields are written only when sent. Answers 200 with the body null; read the project back with GET. Status is changed with PUT /v1/project/{projectID}/status.

Use it to:

  • Sync name, project manager or budget changes from a CRM / PSA

Not for:

  • Status changes → PUT /v1/project/{projectID}/status

Rules across fields:

  • R1: Effective end date (sent, else stored) must be on or after the effective start date (sent, else stored); checked only when at least one date is sent.

Behaviour:

  • Idempotent.
  • Safe to retry only under a condition. Repeating the same body gives the same state, but omitted Description / ContactID are cleared each time.
  • Side effect: Writes the project row (last-modified fields set), saves invoice settings, adds the project manager to the resource group, and notifies contract customers, task search and sales-share read models.
  • Side effect: When project dates are locked to the task plan, Save recalculates the project dates from the task plan.
  • Changes are visible to the next read immediately.

Call first: GET /v1/project/{projectID}

Next: GET /v1/project/{projectID}

See also: PUT /v1/project/{projectID}/status

Not yet documented: behaviour → side effects; examples.

curl -X PUT "https://app3.timelog.com/your-account/api/v1/project/123" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
  "Name": "John Doe",
  "ProjectNo": "example_string",
  "CustomerID": 123,
  "ContactID": 123,
  "Description": "null",
  "DepartmentID": "null",
  "ProjectManagerID": 123,
  "AccountManagerID": "null",
  "PartnerID": "null",
  "ProjectTypeID": "null",
  "ProjectCategoryID": "null",
  "ProjectStartDate": "null",
  "ProjectEndDate": "null",
  "BudgetWorkHours": "null",
  "BudgetWorkAmount": "null",
  "LanguageID": "null"
}'
"null"
PUT
/v1/project/{projectID}
PUT
Base URLstring

Target server for requests. Edit to use your own host.

Bearer Token
Bearer Tokenstring
Required

OAuth 2.0 bearer token, sent as Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp). The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user. > Not yet documented: how to obtain a token. > Not yet documented: token lifetime.

OAuth 2.0 bearer token, sent as Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp). The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user. > Not yet documented: how to obtain a token. > Not yet documented: token lifetime.
path
projectIDinteger
Required

The project to update.

Format: int32
Content-Typestring
Required

The media type of the request body

Options: application/json
Namestring
Required

Project name. Always written.

ProjectNostring

Project number. Written only when non-empty AND the project-number auto-number series is off; otherwise ignored. - Empty / omitted — Number unchanged. - Project-number auto-number is active — Number unchanged, no error.

CustomerIDinteger
Required

The project's customer. Always written; changing it runs ProjectCanChangeCustomer. Refers to CustomerRead.CustomerID. Look up valid values with GET /v1/customer. Valid values come from GET /v1/customer.

Format: int32
ContactIDinteger

Customer contact. Always written: omitted = 0 = the contact is removed. Refers to ContactRead.ContactID. Default: 0 (clears the contact) - Omitted — 0 is written — the stored contact is cleared.

Format: int32
Descriptionstringnull

Project description. Always written: omitted = null = the description is cleared. - Omitted or null — null is written — the stored description is cleared.

DepartmentIDintegernull

Handling department. Written only when sent and the Departments feature is on. Refers to DepartmentRead.DepartmentID. Look up valid values with GET /v1/department. - Feature Departments is off — ignored Valid values come from GET /v1/department.

Format: int32
ProjectManagerIDinteger
Required

Project manager, as a USER ID (existing, active). Always written. Refers to UserRead.UserID. Look up valid values with GET /v1/user. Valid values come from GET /v1/user.

Format: int32
AccountManagerIDintegernull

Account manager, as a USER ID. Written only when sent and the AccountManager feature is on. Cannot be cleared (null = keep). Refers to UserRead.UserID. Look up valid values with GET /v1/user. - Feature AccountManager is off — ignored Valid values come from GET /v1/user. > Not yet documented: description.

Format: int32
PartnerIDintegernull

Partner, as a USER ID. Written only when sent and the Partner feature is on. Refers to UserRead.UserID. Look up valid values with GET /v1/user. - Feature Partner is off — ignored Valid values come from GET /v1/user.

Format: int32
ProjectTypeIDintegernull

Project type. Written only when sent and the ProjectType feature is on. Refers to ProjectTypeRead.ProjectTypeID. - Feature ProjectType is off — ignored

Format: int32
ProjectCategoryIDintegernull

Project category. Written only when sent and the ProjectCategory feature is on. Refers to ProjectCategoryRead.ProjectCategoryID. - Feature ProjectCategory is off — ignored

Format: int32
ProjectStartDatestringnull

New start date (date part compared). Rejected (400) unless the ProjectSchedule feature is on and LockProjectDateWithTaskPlan is off. Time zone: not yet documented. - ProjectSchedule feature off — 400 "ProjectStartDate cannot be updated because the Project schedule feature is not active on this site." - LockProjectDateWithTaskPlan feature on — 400 "…project dates are locked to the task plan on this site." > Not yet documented: constraints → timezone.

Format: date-time
ProjectEndDatestringnull

New end date (stored as ContractEndDate). Same feature gates as ProjectStartDate. Must be on or after the effective start date. Time zone: not yet documented. - End before start (both sent) — 400 validation "ProjectEndDate must be on or after ProjectStartDate." - Only one date sent and it crosses the stored other date — 400 Code 400, same message. > Not yet documented: constraints → timezone.

Format: date-time
BudgetWorkHoursnumbernull

Budgeted work hours; null = keep. Unit: hours

Format: double
BudgetWorkAmountnumbernull

Budgeted work amount; null = keep. Unit: project currency > Not yet documented: constraints → unit.

Format: double
LanguageIDintegernull

Invoice language; written only when sent and InvoiceMultilingual is on. - Any other value — Validation (400). - Feature InvoiceMultilingual is off — ignored Allowed values: | Value | Name | Meaning | |---|---|---| | 1 | Danish | Invoices in Danish. | | 2 | English | Invoices in English. | | 4 | Swedish | Invoices in Swedish. | | 15 | Norwegian | Invoices in Norwegian. | | 16 | German | Invoices in German. | | 17 | Dutch | Invoices in Dutch. | | 18 | French | Invoices in French. |

Format: int32 • Options: 1, 2, 4, 15, 16, 17, 18
Request Preview
Response

Response will appear here after sending the request

Authentication

header
Authorizationstring
Required

Bearer token. OAuth 2.0 bearer token, sent as Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp).

The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user.

Not yet documented: how to obtain a token.

Not yet documented: token lifetime.

Path Parameters

projectIDinteger
Required

The project to update.

Body

application/json
Namestring
Required

Project name. Always written.

ProjectNostring

Project number. Written only when non-empty AND the project-number auto-number series is off; otherwise ignored.

  • Empty / omitted — Number unchanged.

  • Project-number auto-number is active — Number unchanged, no error.

CustomerIDinteger
Required

The project's customer. Always written; changing it runs ProjectCanChangeCustomer.

Refers to CustomerRead.CustomerID. Look up valid values with GET /v1/customer.

Valid values come from GET /v1/customer.

ContactIDinteger

Customer contact. Always written: omitted = 0 = the contact is removed.

Refers to ContactRead.ContactID.

Default: 0 (clears the contact)

  • Omitted — 0 is written — the stored contact is cleared.
Descriptionstring,null

Project description. Always written: omitted = null = the description is cleared.

  • Omitted or null — null is written — the stored description is cleared.
DepartmentIDinteger,null

Handling department. Written only when sent and the Departments feature is on.

Refers to DepartmentRead.DepartmentID. Look up valid values with GET /v1/department.

  • Feature Departments is off — ignored

Valid values come from GET /v1/department.

ProjectManagerIDinteger
Required

Project manager, as a USER ID (existing, active). Always written.

Refers to UserRead.UserID. Look up valid values with GET /v1/user.

Valid values come from GET /v1/user.

AccountManagerIDinteger,null

Account manager, as a USER ID. Written only when sent and the AccountManager feature is on. Cannot be cleared (null = keep).

Refers to UserRead.UserID. Look up valid values with GET /v1/user.

  • Feature AccountManager is off — ignored

Valid values come from GET /v1/user.

Not yet documented: description.

PartnerIDinteger,null

Partner, as a USER ID. Written only when sent and the Partner feature is on.

Refers to UserRead.UserID. Look up valid values with GET /v1/user.

  • Feature Partner is off — ignored

Valid values come from GET /v1/user.

ProjectTypeIDinteger,null

Project type. Written only when sent and the ProjectType feature is on.

Refers to ProjectTypeRead.ProjectTypeID.

  • Feature ProjectType is off — ignored
ProjectCategoryIDinteger,null

Project category. Written only when sent and the ProjectCategory feature is on.

Refers to ProjectCategoryRead.ProjectCategoryID.

  • Feature ProjectCategory is off — ignored
ProjectStartDatestring,null

New start date (date part compared). Rejected (400) unless the ProjectSchedule feature is on and LockProjectDateWithTaskPlan is off.

Time zone: not yet documented.

  • ProjectSchedule feature off — 400 "ProjectStartDate cannot be updated because the Project schedule feature is not active on this site."

  • LockProjectDateWithTaskPlan feature on — 400 "…project dates are locked to the task plan on this site."

Not yet documented: constraints → timezone.

ProjectEndDatestring,null

New end date (stored as ContractEndDate). Same feature gates as ProjectStartDate. Must be on or after the effective start date.

Time zone: not yet documented.

  • End before start (both sent) — 400 validation "ProjectEndDate must be on or after ProjectStartDate."

  • Only one date sent and it crosses the stored other date — 400 Code 400, same message.

Not yet documented: constraints → timezone.

BudgetWorkHoursnumber,null

Budgeted work hours; null = keep.

Unit: hours

BudgetWorkAmountnumber,null

Budgeted work amount; null = keep.

Unit: project currency

Not yet documented: constraints → unit.

LanguageIDinteger,null

Invoice language; written only when sent and InvoiceMultilingual is on.

  • Any other value — Validation (400).

  • Feature InvoiceMultilingual is off — ignored

Allowed values:

ValueNameMeaning
1DanishInvoices in Danish.
2EnglishInvoices in English.
4SwedishInvoices in Swedish.
15NorwegianInvoices in Norwegian.
16GermanInvoices in German.
17DutchInvoices in Dutch.
18FrenchInvoices in French.
Allowed values:12415161718

Responses

Success without content: HTTP 200 with the literal JSON body null. Literal JSON null.