Contracts
Get a contract
/a/Contract_get
Retrieve one active contract in its legacy stored shape, with type name, job-link IDs, and invoiceability added.
When to use this endpoint
Use Contract_get for a complete single-contract legacy record after another call has supplied the ID.
Use Contract_getRequestedDetails when a CRM2 workflow needs selected mapped fields or derived main-page totals.
Authentication and permissions
The request must authenticate with signed API-key fields or a logged-in CRM session. The handler performs no additional page or role-permission check.
The lookup and any write use the authenticated tenant; an ID from another tenant cannot be addressed through this call.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | Integer string | Yes | Contract ID after integer conversion. Omitted and present-but-invalid values produce different errors. |
Example request
$contract = FM_api('Contract_get', [
'id' => '720',
]);
Example response
{
"id": 720,
"name": "North Region Service Contract",
"cdate": "2026-09-25 10:30:00",
"notes": "Renewal review due in September.",
"last_edited": "2026-09-25 10:30:00",
"created_by": "42",
"active": "1",
"customers": "[[0,1042],[1,418]]",
"custom": "{}",
"budget": "25000",
"expiry_date": "2027-09-30",
"number": "CT-720",
"address": "North Region",
"manager": "Service Desk",
"type_id": "8",
"date_completed": null,
"meta": "{\"not_invoiceable\":\"0\"}",
"date_started": "2026-10-01",
"appointments": "[2039001]",
"type_name": "Planned service",
"not_invoiceable": "0"
}
Most database scalars remain JSON strings. customers, custom, meta, and the exact legacy response field appointments are JSON-encoded strings.
deleted and ddate are removed. type_name is blank when the type is missing, and not_invoiceable defaults to numeric 0 when absent from valid metadata.
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":"no ID provided"} | The id key is omitted. |
{"error":"requested contract does not exist"} | The converted ID is zero, unknown, or soft-deleted. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Job IDs come only from type-6
manymanylinks. Jobs that have onlymeetings.contract_idare not included by this endpoint. - The customer/asset rebuild first uses non-deleted jobs whose direct
contract_idmatches. It falls back to legacy links only when there are no such direct jobs, and also adds each linked customer’s direct parent. - Type and contract lookups are tenant-scoped. There is no filtering or pagination.
Side effects
- A dirty customer-cache rebuild replaces type-15/type-16 relationship rows and touches
last_edited. No audit entry is written for that repair.
See it in Fieldmotion
CRM1
The Contracts edit dialog loads a row through the CRM1 Contract client model, which resolves to Contract_get. Filter Network requests while opening Edit Contract.
CRM2
CRM2’s contract detail page uses Contract_getRequestedDetails instead; no direct production CRM2 call to this legacy full-record route was found.
Related endpoints
Contract_getNotesretrieves only notes.Contract_getRequestedDetailsselects mapped fields and can add main-page totals.Contracts_getByIdfetches batches for export workflows.