-
-
Notifications
You must be signed in to change notification settings - Fork 0
Home
MC0RE edited this page Aug 18, 2026
·
11 revisions
Laravel SDK for the Teamleader Focus API.
| Guide | Notes |
|---|---|
| Installation & Configuration | Composer install, .env setup, OAuth flow |
| Usage | Common patterns, filtering, pagination, error handling |
| Token Storage & Security | How tokens are stored, refreshed, and how to harden for production |
| Resource | SDK key | Notes |
|---|---|---|
| Closing Days | closingDays |
Always requests pagination metadata; two filters only, no sorting; getCommonHolidays() needs ext-calendar for Easter Monday |
| Currencies | currencies |
|
| Custom Fields | customFields |
Endpoint customFieldDefinitions; deal fields return context: sale, normalised to deal
|
| Day Off Types | dayOffTypes |
list() takes no arguments and throws if given any |
| Days Off | daysOff |
|
| Departments | departments |
|
| Document-Templates | documentTemplates |
Requires department_id + document_type
|
| Email Tracking | emailTracking |
|
| Notes | notes |
delete() supported since v1.2.8; meeting subject type; no info()
|
| Teams | teams |
|
| User Schedules | userSchedules |
Read-only; max 7-day range; supersedes deprecated users.getWeekSchedule
|
| Users | users |
me() response now includes teams
|
| Work Types | workTypes |
| Resource | SDK key | Notes |
|---|---|---|
| Addresses | addresses |
|
| Business Types | businessTypes |
|
| Companies | companies |
Only custom_fields is a valid list include; info takes related_companies / related_contacts. No name filter β byName() throws, use search()
|
| Contacts | contacts |
Only custom_fields is a valid list include; info takes none |
| Tags | tags |
No filters; sort is fixed to tag ascending |
| Resource | SDK key | Notes |
|---|---|---|
| Deal Phases | dealPhases |
|
| Deal Pipelines | dealPipelines |
Requests pagination metadata |
| Deal Sources | dealSources |
|
| Deals | deals |
create()/update() accept purchase_order_number; sorts on created_at / weighted_value only |
| Lost Reasons | lostReasons |
|
| Orders | orders |
|
| Quotations | quotations |
No client-side validation on update()
|
| Resource | SDK key | Notes |
|---|---|---|
| Activity Types | activityTypes |
|
| Call Outcomes | callOutcomes |
|
| Calls | calls |
delete() supported since v1.2.8 |
| Calendar Events | calenderEvents |
Note: typo in SDK key β one 'a' |
| Meetings | meetings |
group_id filter; project_id/group_id linkage (not with milestone_id) |
| Resource | SDK key | Notes |
|---|---|---|
| Commercial Discounts | commercialDiscounts |
Response objects carry id since July 2026 |
| Credit Notes | creditnotes |
Note: lowercase 'n' in the SDK key. Read-only β no write endpoints exist |
| Invoices | invoices |
expected_payment_method validation; ubl/xrechnung; listDrafts(); untitled grouped_lines sections must omit the section key |
| Payment Methods | payment_methods |
Note: underscore SDK key |
| Payment Terms | payment_terms |
Note: underscore SDK key. list() takes no arguments; meta.default is always returned |
| Subscriptions | subscriptions |
|
| Tax Rates | taxRates |
|
| Withholding Tax Rates | withholdingTaxRates |
| Resource | SDK key | Notes |
|---|---|---|
| Bookkeeping Submissions | bookkeepingSubmissions |
Requires a subject filter |
| Expenses | expenses |
Read-only aggregate view |
| Incoming Credit-Notes | incomingCreditNotes |
create() posts to .add
|
| Incoming Invoices | incomingInvoices |
create() posts to .add
|
| Receipts | receipts |
create() posts to .add
|
| Resource | SDK key | Notes |
|---|---|---|
| Price Lists | priceLists |
|
| Product Categories | productCategories |
|
| Products | products |
create() posts to .add
|
| Units of Measure | unitsOfMeasure |
list() takes no arguments and throws if given any; no info() β use findById(). All helpers are client-side |
Requires Teamleader::accounts()->isUsingProjectsV2() to return true.
| Resource | SDK key | Notes |
|---|---|---|
| External Parties | external_parties |
Note: underscore SDK key |
| Groups | groups |
update() billing_method uses {value, update_strategy}
|
| Materials | materials |
No delete(); create() returns HTTP 201; parent_fixed_price billing |
| Project Lines | projectLines |
list() throws without project_id
|
| Project Tasks | projectTasks |
work_type_rate requires work_type_id
|
| Projects | projects |
Endpoint projects-v2/projects; webhooks are nextgenProject.*; alias nextgenProjects
|
For accounts that have not yet migrated to Projects v2.
| Resource | SDK key | Notes |
|---|---|---|
| Legacy Milestones | legacyMilestones |
|
| Legacy Projects | legacyProjects |
Endpoint is the bare projects.*; webhooks are project.*. forCustomer() is (id, type) β id first; sorts on due_on / title / created_at only |
| Resource | SDK key | Notes |
|---|---|---|
| Plannable Items | plannable_items |
Read-only; use infoBySource() when task UUID known |
| Reservations | reservations |
Duration unit must be minutes
|
| User Availability | user_availability |
list() throws; use daily() / total()
|
| Resource | SDK key | Notes |
|---|---|---|
| Files | files |
upload() / download() return URLs β you transfer the file. Subject types differ between list and upload
|
| Resource | SDK key | Notes |
|---|---|---|
| Tasks | tasks |
Standalone tasks β not the same as Project Tasks |
| Resource | SDK key | Notes |
|---|---|---|
| Mail Templates | mailTemplates |
list() throws without type
|
| Resource | SDK key | Notes |
|---|---|---|
| Ticket Status | ticketStatus |
Read-only; all helpers client-side |
| Tickets | tickets |
Assignee must be user; links to milestone_id or project_id, not both |
| Resource | SDK key | Notes |
|---|---|---|
| Time Tracking | timeTracking |
create() posts to .add; three recording variants; sorts on starts_on only; nextgenTask is valid for writes but not as a filter |
| Timers | timers |
stop() creates a time tracking entry |
| Resource | SDK key | Notes |
|---|---|---|
| Accounts | accounts |
list() and info() throw; use getProjectsVersion() / isUsingProjectsV2(). There is no getDefaultId()
|
| Cloud Platforms | cloudPlatforms |
Valid types: invoice, quotation, ticket
|
| Migrate | migrate |
list() throws; three translation methods |
| Webhooks | webhooks |
URL must be HTTPS; payload at subject.id; list() takes no arguments |
A few behaviours that appear across many resources and are worth knowing before you start.
Since v2.2.0, most of these fail loudly instead of silently. The API answers
200 to unrecognised filter keys, unknown includes and wrong-shaped sort objects,
returning plausible but wrong data. The SDK now rejects them client-side.
- Unsupported filter keys throw β the endpoint would have ignored them and returned everything
- Unsupported sort fields throw β likewise ignored by the API, which then returns records in its own order
-
Filtering and sorting have separate vocabularies β a field being sortable does not make it filterable.
namesorts on Companies but cannot be filtered on -
statusfilters are arrays β except on Companies and Contacts, where the API declares a string enum -
The API returns no pagination metadata by default β no total, no page count. The end of a list is a page shorter than the page size, so a full final page costs one extra empty request. Resources that send
includes=paginationget ametablock; the rest do not -
update()injectsidinto the body β you don't need to include it in$data -
Sideloading parameter is
includes(plural) β notinclude. Passing the singular form is silently ignored by the API -
Includes are per-endpoint β
listandinfofrequently accept different sets, and some endpoints accept none at all -
array_filter()without flags stripsnullβ use explicit null-preservation if clearing fields (fixed in v1.2.6) -
Project linkage is exclusive β on Meetings and Tickets,
group_id/project_id(new projects) cannot be combined withmilestone_id(legacy) -
429 always throws
RateLimitExceededExceptionβ catch it before the genericTeamleaderException -
The rate limiter waits, then throws β up to
rate_limiting.max_wait_ms(default 5000). In a queue worker, catch andrelease($e->getRetryAfter())rather than raising the cap - Rate limiter state must be Redis-backed β file cache does not coordinate across Horizon workers
See CHANGELOG for the full history.
| Version | Highlights |
|---|---|
| v2.2.1 | Removed the phantom name and company_number filters on Companies (byName() returned every company); added the missing filter guards on Closing Days and Legacy Projects; getCommonHolidays() no longer fatals without ext-calendar; README, SECURITY and CONTRIBUTING corrected |
| v2.2.0 | Silent-failure sweep: unsupported filters, sort fields, subject types and includes now throw; sideloading parameters corrected; rate limiter waits for a slot, counts failed requests and reads X-RateLimit-Remaining; July 2026 API changes (price_list_id, commercialDiscounts id) |
| v2.1.1 | Fixed TokenService caching live Carbon objects (__PHP_Incomplete_Class on every entry point); completed facade annotations |
| v2.1.0 | Laravel 13 and PHP 8.5 support |
| v2.0.0 | Breaking: dropped Laravel 10 and 11 (unpatched CVEs; Composer advisories block installation) |
| v1.2.8 | API changelog catch-up (MarβJun 2026): new calls.delete, notes.delete, and userSchedules resource; field/enum additions across Meetings, Notes, Materials, Invoices, Tickets |
| v1.2.7 | Fixed new-Projects Groups resource base path (404 on all group calls) |
| v1.2.6 | Fixed array_filter() null-stripping bug (field clears now work correctly) |
| v1.2.5 | Fixed strict_types TypeError on rate limit reset time |
| v1.2.4 | Redis-backed rate limiter; 429 always throws regardless of config |
| v1.2.3 | Fixed includes (plural) sideloading parameter |
| v1.1.5 | Fixed token storage schema mismatch; removed migration requirement |
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