patchContractEvent
patchContractEvent
Overview
Updates fields on an existing contract event without needing to delete and recreate it. This method requires the ContractEvents:Update permission to be associated with your role.
It performs the same function as editing an event on the Contracts → Events screen — the same row locking, audit trail, and downstream effects (keeping the contract's own details in sync, rebuilding allocation history) apply as when the edit is made on screen.
Permissions
The permission for this api is stored in [gb_api_permissions]permission as 'ContractEvents:Update'.
Only roles with a stored link to this permission in [gb_role_api_permissions] can run this api. By default, only the super role is granted this permission.
HTTP Method
Use the HTTP Method 'PATCH' for consuming this web service.
URL Examples
https://api.demo.catch-e.com/fm/contract/events/{contract_event_id} https://api.{environment}.catch-e.com/fm/contract/events/{contract_event_id} https://api.test.catch-e.com/fm/contract/events/{contract_event_id} Input Fields (Header)
Path Parameters
Input Fields (JSON)
Send only the fields you want to change. Any field left out of the request is left untouched.
{ "event_date": "2026-09-30", "due_date": "2026-09-30", "description": "Updated description", "contract_event_description_id": 100010, "event_value": "Updated value", "contract_event_value_id": 100015, "event_amount": 1234.56, "record_id": 100017, "table_name": "fm_client_cost_centres", "contract_event_action_id": 100001, "completed_flag": "no", "user_id": 11244 } (Illustrative values only — not real data.)
The following fields cannot be changed via this endpoint: attachment_id (attachments are managed through the separate attach/detach endpoints), message_type (create-only), status_flag (use the delete endpoint to remove an event), and last_edit (always server-set from the audit user and current time).
contract_id and posting_class_id are immutable. They don't need to be sent, but if sent, the value must match the event's stored value or the request is rejected (see Error Response Details).
Linked value and description records
Two pairs of fields work the same way:
Send
event_valueon its own to set free text directly. Sendcontract_event_value_idto have the API deriveevent_valuefrom that record instead — the derived text overrides anyevent_valuein the same request. (Exception: for Cost Centre, Division and Driver events,event_valuealways comes fromrecord_id, regardless ofcontract_event_value_id.)Send
descriptionon its own to set free text directly. Sendcontract_event_description_idto have the API derivedescriptionfrom that record instead — the derived text overrides anydescriptionin the same request.
Cost Centre, Division and Driver events
For these three event types, record_id (together with table_name) identifies the linked Cost Centre, Division or Driver record, and the API always derives event_value from it — any event_value sent for these events is ignored.
The linked record must belong to the contract's own client. For Driver events only, a record belonging to a different client is also accepted if that client shares the contract client's client group; Cost Centre and Division records must belong to the contract's own client specifically. A record_id that doesn't exist, isn't active, or belongs to a client outside this scope is rejected — see Error Response Details.
User assignment
user_id identifies who the event is assigned to (this is separate from the audit trail — see the Audit-User-Id header above, which records who made the change, not who the event is assigned to).
Omitted, empty, or
nullleaves the current assignment unchanged.Sending the event's own current assignment is always accepted and writes nothing, regardless of whether that account would otherwise qualify below — existence is still enforced.
A changed value must identify an existing user account. If it doesn't, the request is rejected with
422 user_id.noRecordFound.A changed value must also be an account the Contracts → Events screen's own user picker would offer — that is, all of the following must be true: the account's access type is not
externalorweb-services, its login is not the literaladminaccount, its status is notdeletedorsuspended, and if the account holds the super role, the caller making the request must also hold the super role. If any of these fail, the request is rejected with422 user_id.invalidInputand the whole request is rolled back — no other fields in the same call are written either.The audit author recorded against the change is unaffected by
user_id— it's still the acting user (or theAudit-User-Idheader value, if supplied).
Effect on the contract
If the event you're editing is the contract's most recent active event of certain types (Registration Number, Cost Centre, Division, Driver, or Location), the API recomputes that contract's corresponding field and updates it to match, rebuilds the contract's Cost Centre / Division / Driver allocation history, and syncs the change to Salesforce where Salesforce Apex sync is enabled for the tenant. Editing an older event of the same type does not change the contract.
Response Details
Success
200 - OK
{ "_links": { "self": { "href": "https://api.catch-e.loc/fm/contract/events/117157" } }, "_embedded": { "contract_event_id": 117157, "contract_id": 100237, "posting_class_id": 4, "event_date": "2026-09-30", "due_date": "2026-09-30", "description": "Updated description", "event_value": "Updated value", "event_amount": 1234.56, "record_id": 100017, "table_name": "fm_client_cost_centres", "contract_event_action_id": 100001, "completed_flag": "no", "user_id": 11244, "status_flag": "active" } } Error Response Details
{ "validation_messages": { "contract_event_action_id": { "noRecordFound": "A textual description of the validation exception" } }, "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html", "title": "Unprocessable Entity", "status": 422, "detail": "Failed Validation" } { "validation_messages": { "posting_class_id": { "invalidRecord": "The posting class for this contract event no longer exists" } }, "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html", "title": "Unprocessable Entity", "status": 422, "detail": "Failed Validation" } 401 Unauthorized
{ "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html", "title": "Unauthorized", "status": 401, "detail": "Unauthorized" } 403 Forbidden
{ "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html", "title": "Forbidden", "status": 403, "detail": "Forbidden" } 406 Not Acceptable
{ "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html", "title": "Not Acceptable", "status": 406, "detail": "Not Acceptable" } 415 Unsupported Media Type
{ "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html", "title": "Unsupported Media Type", "status": 415, "detail": "Not Acceptable" } 500 Internal Error
{ "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html", "title": "string", "status": 599, "detail": "string" } default Unexpected Error
{ "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html", "title": "string", "status": 0, "detail": "string" }