Step 2 — Assistance contracts
Optional, but defines how hours are billed. A contract gives the customer an hours ceiling that interventions consume.
Hierarchy
entity-contract-group → commercial grouping
entity-contract-type → periodicity, renewable, service type
entity-contract → customer contract, with totalHours (ceiling)
Always create in that order: the contract validates that group and type already exist.
2.1 Contract group
POST /gateway/entity-contract-group
Content-Type: application/json
Authorization: Bearer {token}
{
"id": 1,
"description": "Annual retainers"
}
Required: id (numeric, user-defined, > 0) and description (≤50 chars).
Next free number: GET /gateway/entity-contract-group/next-id.
2.2 Contract type
POST /gateway/entity-contract-type
Content-Type: application/json
Authorization: Bearer {token}
{
"id": 1,
"description": "Monthly time-based retainer",
"renewable": true,
"periodicity": 1,
"contractType": 2
}
Required: id (> 0), description (≤50 chars), periodicity and contractType. Optional: renewable.
| Field | Values |
|---|---|
periodicity | 1 = Monthly, 2 = Annual |
contractType | 1 = Per service (PerService), 2 = Per time (PerTime) |
contractType decides whether the hours ceiling matters: per-service contracts do not validate hours; only per-time contracts consume the ceiling. The hours-available endpoint returns this so integrators can skip validation when applicable.
2.3 Customer contract
POST /gateway/entity-contract
Content-Type: application/json
Authorization: Bearer {token}
{
"id": 1,
"entityKeyId": "C0001",
"contractGroupId": 1,
"contractTypeId": 1,
"startDate": "2026-01-01T00:00:00Z",
"endDate": "2026-12-31T00:00:00Z",
"totalHours": 40,
"contractValue": 1200.00,
"contractValueRp": 1200.00,
"contractCostValue": 800.00,
"observation": "Preventive maintenance retainer, 40h/year.",
"canceled": false
}
Required: id (> 0), entityKeyId (≤25 chars, customer must exist), contractGroupId, contractTypeId, startDate and endDate (end cannot be before start — InvalidDateRange).
Useful optional fields: totalHours, contractValue, contractValueRp, contractCostValue, observation (≤4000 chars), covenantId (≤50 chars; if sent, the agreement must exist) and canceled.
Next free number: GET /gateway/entity-contract/next-id.
Relevant endpoints
| Endpoint | Purpose |
|---|---|
GET /gateway/entity-contract?page=&pageSize= | Paginated list |
GET /gateway/entity-contract/{id} | Detail |
GET /gateway/entity-contract/{id}/hours-available | Hours still available (totalHours, availableHours, contractType) |
GET /gateway/entity-contract/{id}/has-documents | Check dependencies before delete |
GET /gateway/entity-contract/next-id | Next free number |
GET /gateway/intervention/by-contract/{contractId} | Interventions charged to the contract |
Deleting a contract with linked orders or interventions returns InUse. Check with has-documents first.
How the ceiling is consumed
The intervention links to the contract via entityContractId and discountTime (hours) is debited. When those hours are actually invoiced, a document reference of type ServiceOrderInterventionHours pointing at the intervention is created — invoiced hours free ceiling again, because they moved from contract consumption to a sale.
Available hours = totalHours - (hours used - hours already invoiced).