-
-
Notifications
You must be signed in to change notification settings - Fork 0
Units of Measure
Read unit of measure definitions in Teamleader Focus.
Units of measure define how products are quantified β pieces, hours, kilograms, metres, etc. They are read-only through the API and must be configured in the Teamleader Focus web interface.
Access via Teamleader::unitsOfMeasure().
unitsOfMeasure.listis the only endpoint. There is noinfo, no filtering, no sorting and no pagination β the specification declares no request body for it at all.list()posts an empty body and returns every unit in one response. All helpers are client-side.Passing arguments to
list()throws since v2.2.0. The method inherits the standardlist(array $filters = [], array $options = [])signature, but the endpoint cannot honour any of it. Before v2.2.0 the arguments were silently discarded β see Why this throws.
unitsOfMeasure
| Capability | Supported |
|---|---|
| Pagination | β Not supported |
| Filtering | β Not supported |
| Sorting | β Not supported |
| Sideloading | β Not supported |
| Creation | β Not supported |
| Update | β Not supported |
| Deletion | β Not supported |
Returns all units in a single response. Takes no arguments.
use McoreServices\TeamleaderSDK\Facades\Teamleader;
$units = Teamleader::unitsOfMeasure()->list();Any argument throws before the request is sent:
Teamleader::unitsOfMeasure()->list([], ['page_size' => 5]);
// InvalidArgumentException: unitsOfMeasure.list does not support pagination.
// Passed: page_size. The endpoint returns every record in a single response,
// so there are no pages to request.Not available. unitsOfMeasure.info does not exist in the API. Calling it throws with a pointer to the alternative:
Teamleader::unitsOfMeasure()->info('unit-uuid');
// InvalidArgumentException: unitsOfMeasure has no info endpoint β list() is the
// only one the API exposes. Use findById() to resolve a single unit from the
// full list.Before v2.2.0 this raised Error: Call to undefined method instead, since neither this class nor its parent defined info().
The method signature offered filtering, sorting and pagination that the endpoint has never supported, and the arguments were quietly dropped:
$first = Teamleader::unitsOfMeasure()->list([], ['page_size' => 5, 'page_number' => 1]);
$second = Teamleader::unitsOfMeasure()->list([], ['page_size' => 5, 'page_number' => 2]);
count($first['data']); // 16 β not 5
$first['data'] === $second['data']; // true β page 2 was page 1That produced a specific and delayed failure. A sync layer that pages through an entity and soft-deletes anything the enumeration didn't mention appeared to work with sixteen units: page 1 came back shorter than the page size, so the pager concluded it had reached the end. That was a coincidence, not correctness. Past the page size, page 2 returns page 1 again, the same records are re-processed forever, and the loop never terminates.
Failing at the call site is the alternative. If you are passing arguments here, the right fix is to stop β the full list is always returned.
There is no
paginate()on this resource. Only Tags and Work Types define one.
All helpers are client-side β they call list() then search or transform in PHP. Each makes one API call.
Case-insensitive exact match, whitespace trimmed. Returns the matching unit array or null.
$unit = Teamleader::unitsOfMeasure()->findByName('piece');
$unit = Teamleader::unitsOfMeasure()->findByName('Hour'); // case-insensitiveReturns the unit with that UUID or null. This is the replacement for the info() endpoint the API doesn't provide.
$unit = Teamleader::unitsOfMeasure()->findById('unit-uuid');Returns flat [id => name] map.
$options = Teamleader::unitsOfMeasure()->asOptions();
// ['uuid-1' => 'piece', 'uuid-2' => 'kilogram', 'uuid-3' => 'hour']Returns a Laravel Collection for fluent manipulation.
$metreLike = Teamleader::unitsOfMeasure()->asCollection()
->filter(fn($u) => str_contains(strtolower($u['name']), 'meter'));Returns true if a unit with that name exists. Calls findByName() internally.
$exists = Teamleader::unitsOfMeasure()->exists('piece');Returns the total number of configured units.
$total = Teamleader::unitsOfMeasure()->count();Each helper triggers its own
list()call. Chaining several βexists()thenfindByName()thencount()β costs three requests against your rate limit budget. Calllist()once and work with the result if you need more than one.
[
'data' => [
['id' => 'uuid', 'name' => 'piece'],
['id' => 'uuid', 'name' => 'kilogram'],
['id' => 'uuid', 'name' => 'hour'],
['id' => 'uuid', 'name' => 'meter'],
],
'headers' => ['X-RateLimit-Remaining' => ['199'], /* ... */],
]headers is added by the SDK on every successful response and carries the rate-limit budget. There is no meta block and no pagination metadata β the complete list is always returned.
$unit = Teamleader::unitsOfMeasure()->findByName('hour');
if (! $unit) {
throw new \Exception("Unit 'hour' not found β configure it in Teamleader first.");
}
Teamleader::products()->create([
'name' => 'Consulting Hour',
'unit_of_measure_id' => $unit['id'],
// ...
]);Units rarely change, and every helper costs a request:
$options = Cache::remember('tl_units_of_measure', 86400, fn () =>
Teamleader::unitsOfMeasure()->asOptions()
);$units = collect(Teamleader::unitsOfMeasure()->list()['data'])
->keyBy(fn ($unit) => strtolower($unit['name']));
$hourId = $units['hour']['id'] ?? null;
$pieceId = $units['piece']['id'] ?? null;use InvalidArgumentException;
// Filtering
try {
Teamleader::unitsOfMeasure()->list(['ids' => ['unit-uuid']]);
} catch (InvalidArgumentException $e) {
// 'unitsOfMeasure.list does not support filtering. Passed: ids. Call list()
// without filters; the endpoint returns every record. See getCapabilities()
// for what this resource supports.'
}
// Pagination
try {
Teamleader::unitsOfMeasure()->list([], ['page_size' => 5, 'page_number' => 2]);
} catch (InvalidArgumentException $e) {
// 'unitsOfMeasure.list does not support pagination. Passed: page_size, page_number. ...'
}
// Sorting
try {
Teamleader::unitsOfMeasure()->list([], ['sort' => 'name']);
} catch (InvalidArgumentException $e) {
// 'unitsOfMeasure.list does not support sorting. Passed: sort. Records come
// back in the order the API chooses.'
}
// No info endpoint
try {
Teamleader::unitsOfMeasure()->info('unit-uuid');
} catch (InvalidArgumentException $e) {
// 'unitsOfMeasure has no info endpoint ... Use findById() ...'
}-
Products β
unit_of_measure_idis set on product creation - Product Categories β Companion reference resource for products
- Price Lists β Companion reference resource for products
- Payment Terms β Another endpoint that takes no arguments and returns everything
- Day Off Types β Same shape
- Webhooks β Same shape
Last Updated: August 2026 β’ SDK Version: 2.2.2 β’ Made with β€οΈ by MCore Services
- Departments
- Users
- Teams
- Custom Fields
- Work Types
- Document Templates
- Currencies
- Notes
- Email Tracking
- Closing Days
- Day Off Types
- Days Off
- User Schedules
- Invoices
- Credit Notes
- Subscriptions
- Payment Methods
- Payment Terms
- Tax Rates
- Withholding Tax Rates
- Commercial Discounts
Next Gen Projects
Legacy Projects