Contracts
Create a legacy contract
/a/Contract_new
Create a legacy contract and optionally move a supplied set of job links into it.
When to use this endpoint
Use Contract_new for CRM1 contract creation or when creating a contract inline from a job edit.
Use Contract_createData for CRM2’s mapped layout workflow, but account for its different metadata and expiry side effects.
Authentication and permissions
The request must establish an authenticated tenant context through signed API-key fields or a CRM session. The handler checks DBP but does not separately require a user ID.
No page or role-permission check is performed. created_by is taken from the authenticated session user.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | String | Yes | Trimmed contract name. Blank and the false-like string "0" are rejected; maximum 64 characters. Names need not be unique. |
notes | String | No | Trimmed notes; defaults to blank. This route does not send mention notices. |
expiry | YYYY-MM-DD string | No | Optional expiry date. Blank stores null. The check verifies only shape, not calendar validity. |
custom | JSON string or value | No | Initial custom values; defaults to an empty array. A string is JSON-decoded, then the value is JSON-encoded for storage. Invalid JSON therefore stores JSON null. |
jobs | JSON array string | No | Job IDs to move into this contract’s type-6 legacy link set. Defaults to []. Each value is integer-converted; IDs and duplicates are not validated. |
address | String | No | Initial address; defaults to blank. |
type_id | Integer string | No | Contract-type ID; defaults to 0 and is not validated. |
Example request
$created = FM_api('Contract_new', [
'name' => 'North Region Service Contract',
'type_id' => '8',
'notes' => 'Renewal review due in September.',
'expiry' => '2027-09-30',
'jobs' => json_encode([2039001, 2039002]),
]);
Example response
{
"id": 720
}
Errors and unusual responses
| Response | Condition |
|---|---|
{"error":"not logged in"} | No authenticated tenant context is available. Invalid signed credentials can be rejected by the dispatcher before the handler runs. |
{"error":"missing or invalid parameter: name"} | The trimmed name is empty or false-like. |
{"error":"parameter too long: name. max length 64 characters"} | The name exceeds 64 Unicode characters. |
{"error":"invalid parameter: expiry"} | A nonblank expiry does not match YYYY-MM-DD. |
Server error/runtime type error | jobs is malformed JSON or decodes to a non-array; the handler passes it directly to array_map. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- The new row starts with
meta.customersDirty=1. If jobs are supplied, a customer/asset rebuild runs immediately from direct jobs or, when none exist, the new legacy links. - Duplicate job IDs can create duplicate relationship rows. A positive nonexistent job ID can create an orphan link.
- The route queues the type-104 worker that later fills
date_completedwhen an expiry becomes due, but does not queue type-10/type-11 contract email notifications.
Side effects
- Inserts the contract, optionally deletes each supplied job’s previous legacy contract link and inserts replacements, may rebuild customer/asset link rows, and writes a contract-created audit entry.
- The row/link/cache/audit/cron operations are not wrapped in one transaction.
See it in Fieldmotion
CRM1
Use Create on the Contracts page, or choose add new from the Contract selector while editing a job. Filter Network requests for Contract_new.
CRM2
No direct production CRM2 call was found. CRM2’s configurable Contracts screens derive Contract_createData from their layout configuration.
Related endpoints
Contract_createDatais CRM2’s mapped create path.Contract_addJobadds one legacy link after creation.Contract_setExpiryDatesynchronizes all expiry schedules.