Create a project

Operation ID: createPsaProject

Group: PSA › Projects

Open in the interactive reference

POST /psa/projects

Retry-safe: A write that accepts Idempotency-Key: repeating a request with the same key replays the stored answer instead of executing twice.

Creates a project. The request also creates the project's time-tracking project and its team. Each employee in teamLeadIds is also given the PSA manager permission role.

The response contains only the new project's id. Read the project with GET psa/projects/:id.

Parameters

Name In Type Required Description
Api-Version header string · v1 yes The API version this request targets, named by its public identifier. Required on every request; omitting it, or naming a version this company does not have, is answered with 400. This document describes version v1.
Idempotency-Key header string no An opaque caller-chosen string (1-255 characters) that makes this request safe to retry. Repeating the same key with an identical request body replays the original response instead of re-executing - but only once that original request succeeded; a failed attempt re-executes on retry. Repeating the key with a different body is answered with 422. A key still in flight is answered with 409 - as is a key whose original request was interrupted before it finished, for a few minutes afterwards, until that attempt is presumed dead. A completed response stays replayable for 24 hours. The key is scoped to the caller, not to the action: reusing it for a different action is answered with 409 or 422 rather than treated independently. On read actions this header is ignored. On write actions that cannot offer the guarantee it is answered with 400 IDEMPOTENCY_NOT_SUPPORTED rather than silently ignored.

Request body

Required, application/json: createPsaProjectInput.

Field Type Required Description
name string yes The display name for the project. Example: "Acme Rollout".
startDate date yes The project start date. Format YYYY-MM-DD. Example: "2026-09-01".
endDate date yes The project end date. Format YYYY-MM-DD. Example: "2027-03-01".
teamStartDate date no The team assignment start date. Defaults to startDate when omitted. Format YYYY-MM-DD.
teamEndDate date no The team assignment end date. Defaults to endDate when omitted. Format YYYY-MM-DD.
teamLeadIds array<number> yes The numeric identifiers of the employees to assign as project and team leads. You must send the list, but it can be empty. There is no endpoint for them. Example: [3845142].
notifyProjectLeads boolean no Whether to notify the assigned project leads.
category any yes The project category. Accepted values: 'Billable', 'Internal', 'NonBillable'. One of: "Billable", "Internal", "NonBillable". Example: "Billable".
status any yes The initial project status. Accepted values: 'Tentative', 'Planning', 'PlanningDraft', 'NotStarted', 'InProgress', 'Paused', 'Completed', 'Cancelled'. One of: "Tentative", "Planning", "PlanningDraft", "NotStarted", "InProgress", "Paused", "Completed", "Cancelled". Example: "NotStarted".
currency string yes The project's default currency. Format ISO 4217, for example 'USD'. Example: "USD".
budgetAmount decimal no The project budget amount, in budgetCurrency.
budgetCurrency string no The currency of budgetAmount. Format ISO 4217, for example 'USD'. It should match the project's default currency.
departmentId number no The numeric identifier of the department to allocate the project to. GET people/departments/:id takes a UUID, so the number cannot be used to fetch the department.
entityId number no The numeric identifier of the legal entity the project belongs to. When salesArrangementId is set, it defaults to the sales arrangement's entity, and a different value fails with the message 'The project's entity must match the entity of its sales arrangement'. GET base/entities lists entities by UUID, so the number cannot be used to look the entity up there.
customerId number no The numeric identifier of a customer to link directly to the project. It is ignored when salesArrangementId is set. GET accounting/customers/:id takes a UUID, so the number cannot be used to fetch the customer.
enforceActivityDateRange boolean no Whether time entries must fall within the project's startDate and endDate.
invoiced boolean no Whether to mark the project as invoiced. Defaults to false.
notes string no Free-form notes about the project.
opportunityId number no The numeric identifier of a CRM opportunity to link to the project. GET crm/opportunities/:id takes a UUID, so the number cannot be used to fetch the opportunity.
code string no A code for the project.
quoteId number no The numeric identifier of the CRM quote the project is created from. GET crm/quotes/:id takes a UUID, so the number cannot be used to fetch the quote.
salesArrangementId number no The numeric identifier of a sales arrangement that links the project to billing terms. When set, the direct customerId link is not persisted. There is no endpoint for it.
salesRepresentativeId number no The numeric identifier of the sales representative, an employee. There is no endpoint for it.
projectTypeId number no The numeric identifier of the project type. GET psa/project-types/:id takes a UUID, so the number cannot be used to fetch the project type.
externalLink string no A link to an external system for this project.
projectSettings object no The timetracking settings for the project. Created only when at least one setting is provided.
projectSettings.disableTimetrackingOnHolidays boolean no
projectSettings.disableTimetrackingOnWeekends boolean no
projectSettings.maxHoursPerDay decimal no
projectSettings.maxHoursPerMonth decimal no
projectSettings.maxHoursPerWeek decimal no
projectSettings.billableHoursPerDay decimal no
projectSettings.notifyNewMembers boolean no
projectSettings.requireNotes boolean no
{
  "name": "Acme Rollout",
  "startDate": "2026-09-01",
  "endDate": "2027-03-01",
  "teamStartDate": "2026-01-15",
  "teamEndDate": "2026-01-15",
  "teamLeadIds": [
    3845142
  ],
  "notifyProjectLeads": true,
  "category": "Billable",
  "status": "NotStarted",
  "currency": "USD",
  "budgetAmount": "19.99",
  "budgetCurrency": "<budgetCurrency>",
  "departmentId": 0,
  "entityId": 0,
  "customerId": 0,
  "enforceActivityDateRange": true,
  "invoiced": true,
  "notes": "<notes>",
  "opportunityId": 0,
  "code": "<code>",
  "quoteId": 0,
  "salesArrangementId": 0,
  "salesRepresentativeId": 0,
  "projectTypeId": 0,
  "externalLink": "<externalLink>",
  "projectSettings": {
    "disableTimetrackingOnHolidays": true,
    "disableTimetrackingOnWeekends": true,
    "maxHoursPerDay": "19.99",
    "maxHoursPerMonth": "19.99",
    "maxHoursPerWeek": "19.99",
    "billableHoursPerDay": "19.99",
    "notifyNewMembers": true,
    "requireNotes": true
  }
}

Response

200 Success

Under data (createPsaProjectOutput):

Field Type Required Description
id string yes The unique identifier for the created project. Example: "3b7d5e9c-1a24-4f80-9c6d-2e8f70b13a45".
{
  "data": {
    "id": "3b7d5e9c-1a24-4f80-9c6d-2e8f70b13a45"
  },
  "diagnostics": [
    {
      "code": "<code>",
      "message": "<message>",
      "severity": "error",
      "path": [
        "<path>"
      ],
      "details": "<details>"
    }
  ]
}

Response headers

Errors

Code Status
IDEMPOTENCY_KEY_CONFLICT 409
IDEMPOTENCY_PAYLOAD_MISMATCH 422

Statuses