Update an existing manual position
What this endpoint does: Updates an existing manual position. Supports partial updates - you only need to provide the fields you want to change.
How partial updates work:
- Only provided fields are updated
- Fields not included in the request remain unchanged
- To remove a category, set categoryId to null
- Description can be updated with new HTML or Markdown content
Description updates:
- If description is provided, it will be converted from HTML/Markdown to internal IDoc format
- If description is not provided, the existing description remains unchanged
- Empty descriptions are allowed
Validation rules:
- Position must exist and belong to the specified project
- Name must not be empty if provided
- Price must be greater than or equal to 0 if provided
- Category ID must be a valid UUID if provided (or null to remove)
- Project must exist and user must have access
Permission requirements:
- Requires Projects.manageManualPositions permission
- Requires api scope
- Requires read access to the project
Use cases:
- Correct pricing or descriptions
- Update position details before creating quotes
- Reassign positions to different categories
- Modify positions based on client feedback
PATCH
/projects/{projectId}/manual-positions/{id}Authorization
AuthorizationOAuth2 access token · headerrequiredScopes:
api:writeor
AuthorizationBearer token (JWT) · headerrequiredPath parameters
projectIdstringrequiredidstringrequiredQuery parameters
fieldsToReturnstringComma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
responseShapestringAdvanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
Allowed:
compactfullHeader parameters
LT-Response-ShapestringAdvanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
Allowed:
fullRequest body
requiredapplication/jsoncategoryIdobject | nullUpdated category ID. Provide a valid UUID to assign a category, or null to remove the category assignment. Only provide this field if you want to change the category.
descriptionstringUpdated description. Can be provided as HTML or Markdown - the system will automatically detect and convert the format. Only provide this field if you want to change the description. To clear the description, provide an empty string.
discountAmountnumberDiscount amount to apply. For percentage discounts, this is the percentage (0-100). For fixed discounts, this is the amount in workspace currency. Set to 0 to remove the discount.
min 0
discountTypestringType of discount. "Percentage" applies a percentage reduction, "Fixed" applies a fixed amount reduction. Required when discountAmount is provided.
Allowed:
FixedPercentagenamestringUpdated name of the manual position. Only provide this field if you want to change the name.
pricenumberUpdated price in the workspace currency. Must be a non-negative number. Only provide this field if you want to change the price.
min 0
Responses
200
categoryIdobject | nullCategory ID if the position is assigned to a category, or null if no category is assigned. Categories help organize related positions together.
createdAtstringrequiredISO 8601 timestamp indicating when the position was created
descriptionstringrequiredDescription of the position in HTML format. The description is converted from internal IDoc format to HTML for API responses. May be empty if no description was provided.
discountAmountnumberrequiredDiscount amount applied to this position. 0 means no discount.
discountTypestringrequiredType of discount: "Fixed" (fixed amount) or "Percentage" (percentage of price)
Allowed:
FixedPercentageidstringrequiredUnique identifier of the manual position (UUID)
namestringrequiredName of the manual position as it appears in quotes and invoices
pricenumberrequiredPrice of the position in the workspace currency (net price before taxes)
sortnumberrequiredSort order of the position. Lower numbers appear first. Used to control the display order in project planning views, quotes, and invoices.
401Unauthorized - Invalid or missing authentication token
403Forbidden - Insufficient API scopes or permissions
Request
curl -X PATCH "https://leadtime.app/api/public/projects/string/manual-positions/string" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"categoryId": "550e8400-e29b-41d4-a716-446655440000",
"description": "<p>Professional consulting services</p>",
"discountAmount": 10,
"discountType": "Percentage",
"name": "Consulting Hours",
"price": 150
}'const response = await fetch("https://leadtime.app/api/public/projects/string/manual-positions/string", {
method: "PATCH",
headers: {
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({
"categoryId": "550e8400-e29b-41d4-a716-446655440000",
"description": "<p>Professional consulting services</p>",
"discountAmount": 10,
"discountType": "Percentage",
"name": "Consulting Hours",
"price": 150
})
});import requests
response = requests.patch(
"https://leadtime.app/api/public/projects/string/manual-positions/string",
headers={
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json"
},
json={
"categoryId": "550e8400-e29b-41d4-a716-446655440000",
"description": "<p>Professional consulting services</p>",
"discountAmount": 10,
"discountType": "Percentage",
"name": "Consulting Hours",
"price": 150
},
)Response
{
"categoryId": "550e8400-e29b-41d4-a716-446655440000",
"createdAt": "2024-01-01T00:00:00Z",
"description": "<p>Professional consulting services</p>",
"discountAmount": 10,
"discountType": "Fixed",
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Consulting Hours",
"price": 150,
"sort": 0
}Unauthorized - Invalid or missing authentication token
Forbidden - Insufficient API scopes or permissions