Skip to content

Update Contact

This endpoint updates an existing contact by ID.

PUT /v1/contacts/contacts/:id

Include your API key in the x-api-key header. See Authentication.

contacts:write
ParameterTypeRequiredDescription
idstringYesContact ID to update.

Send only the fields you want to change. Omitted fields keep their current values. The server validates the request body. A body that fails validation returns 400 with {"message": "Error parsing body"}. Unknown fields are ignored. The body must be a JSON object, even if it is empty ({}).

FieldTypeRequiredDescription
type"EXTERNAL" | "INTERNAL"NoNew contact record type. Omit it to keep the current type.
userIdstring | nullWhen type is "INTERNAL"ID of the user to link. Required whenever the body contains "type": "INTERNAL", even if the contact is already internal. Accepts the user ID (for example user-a) or the internal <tenantId>#<userId> form. On an internal contact, sending userId without type links a different user. Ignored for external contacts.
emailstringWhen type is "EXTERNAL"Contact email address. Required and must not be blank whenever the body contains "type": "EXTERNAL", even if the contact is already external. This includes switching an internal contact to external. Ignored for internal contacts.
namestringNoNew contact name (external contacts only). When omitted on an internal-to-external switch, the contact keeps the name it took from the linked user. Ignored for internal contacts.
clientIdstring | nullNoNew client. Send null to remove the client. The client must exist in your account and must not be deleted.
confirmedProjectIdsToRemovestring[]NoProject IDs from a 409 client-change preview. Confirms that those project associations can be removed.
descriptionstring | nullNoNew contact description. Send null to clear it.
isArchivedbooleanNoNew archive state.
hasPortalAccessbooleanNoTurns portal access on or off.
{
"clientId": "client-ghi123jkl4567890",
"type": "EXTERNAL",
"name": "Contact Name",
"email": "placeholder@example.com",
"description": "Updated description",
"hasPortalAccess": true
}
Terminal window
curl -X PUT "https://api.crewsum.com/v1/contacts/contacts/contact-jkl123mno4567890" \
-H "x-api-key: $CREWSUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"clientId": "client-ghi123jkl4567890",
"type": "EXTERNAL",
"name": "Contact Name",
"email": "placeholder@example.com",
"description": "Updated description",
"hasPortalAccess": true
}'

On success, the API returns HTTP 200 OK. The response has no data field.

{
"message": "Successfully updated contact: contact-jkl123mno4567890"
}

When a client change needs confirmation, the API returns HTTP 409 Conflict:

{
"message": "Changing the client removes project associations",
"data": {
"projectsToRemove": [
{
"projectId": "project-mno123pqr4567890",
"projectName": "Project Name",
"clientId": "client-old0000000000000",
"clientName": "Old Client"
}
]
}
}
  • Every successful update sets updatedAt to the current time.
  • Changing type to "INTERNAL" links the user in userId, clears the stored contact email, and sets the name to the user’s full name. Responses then return the user’s email.
  • Changing type to "EXTERNAL" removes the linked user and stores the email from the body.
  • An internal contact’s name is refreshed from the linked user’s full name on every update.
  • Sending clientId: null removes the contact’s client and keeps all of its project associations.
  • Moving the contact to a different client can conflict with its project associations. If the contact is associated with projects that belong to other clients, the API returns 409 with those projects in data.projectsToRemove. Nothing is written.
  • To go ahead, send the same update again with confirmedProjectIdsToRemove set to exactly the project IDs from the preview. The client change and the removal of those associations are saved together. The projects themselves are not changed. If the list of affected projects has changed since the preview, the API returns a new 409.
  • Associations removed this way are deleted permanently. No associations.removed webhook event is sent for them.
  • After the contact is updated, Crewsum attempts to send the project-contacts.updated webhook event. A webhook failure does not change the response.
StatusMeaning
400{"message": "Error parsing body"}. The body failed validation: it is missing or not a JSON object, a field has the wrong type, "type": "INTERNAL" was sent without userId, or "type": "EXTERNAL" was sent without a non-blank email. The response does not say which field failed.
400{"message": "ContactID missing"}. The ID path segment is empty.
400{"message": "Client must belong to the current tenant"}. clientId does not exist in your account, or the client is deleted.
400{"message": "Internal contact user must belong to the current tenant"}. No user with this userId exists in your account.
400{"message": "Email is required for external contacts"}. The contact is external and the update would leave it without an email, for example because email is an empty string.
409{"message": "Changing the client removes project associations", "data": {...}}. The client change needs confirmation. See Behavior.
500{"message": "Error updating contact: <id>"}. Crewsum could not update the contact. This is also the response when no contact with this ID exists in your account.

Authentication and scope failures are described in Errors.