-
Notifications
You must be signed in to change notification settings - Fork 170
/
apiary.apib
9767 lines (8064 loc) · 393 KB
/
apiary.apib
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
FORMAT: 1A
HOST: https://api.focus.teamleader.eu
# Teamleader API
This is the documentation of the Teamleader Focus API. If you have any feedback, or are you missing functionality to support your use case? Let us know via [api@teamleader.eu](mailto:api@teamleader.eu).
## AP-What?
An API is an Application Programming Interface. It is used to programmatically interact with Teamleader, integrate with other software tools or add custom functionalities to Teamleader.
Before creating a new integration, make sure to check our existing integrations built by our community in our [Teamleader Marketplace](https://marketplace.focus.teamleader.eu).
## General principles
### Endpoints
Our API consists of HTTP RPC-style methods, in the form of `https://api.focus.teamleader.eu/resource.action`.
We chose this action based approach over the more popular REST, because it enables us to have domain related actions on resources such as `invoices.book`, `timetracking.start` and `timetracking.stop`.
### Transport Security
We expect all network clients to make use of TLS 1.2 or higher when connecting to our infrastructure.
### Requests
All methods must be called using HTTPS. Data is passed as JSON data in a POST request.
```
POST https://api.focus.teamleader.eu/invoices.list
{
"filter": {
"department_id": "9d4096c3-813f-4bd5-b3c4-4091807b5b74"
},
"page": {
"size": 50,
"number": 3
}
}
```
### Responses
Both single objects and collections are returned as JSON objects, keyed by `data` in the top level of a JSON object:
```json
{
"data": {
"id": "f1dfb84c-3c29-4548-9b9b-9090a080742a",
"first_name": "Erlich",
"last_name": "Bachman",
"salutation": "Mr",
"email": "info@piedpiper.eu"
}
}
```
Responses may also contain meta data for pagination.
Sample response with pagination:
```json
{
"data": [
{
"id": "f1dfb84c-3c29-4548-9b9b-9090a080742a",
"first_name": "Erlich",
"last_name": "Bachman",
"salutation": "Mr",
"email": "info@piedpiper.eu"
},
{
"id": "f1dfb84c-3c29-4548-9b9b-9090a080742b",
"first_name": "John",
"last_name": "Doe",
"salutation": "Mr",
"email": "john@piedpiper.eu"
}
],
"meta": {
"page": {
"size": 20,
"number": 2
},
"matches": 200
}
}
```
### Errors
Errors are always returned as an array of error objects, keyed by `errors` in the top level of a JSON object:
```json
{
"errors": [
{
"title": "Company name must not be empty"
}
]
}
```
### Rate limiting
To ensure a fast and predictable experience for everyone, we limit the number of calls your integration (or client id) can make to a specific Teamleader account.
To calculate this, we use the **sliding window** principle: before allowing requests, we check how many requests your integration made in the **last minute**.
If you would cross your limit, we deny the request by replying with a specific HTTP error.
`HTTP 429 Too Many Requests`
You will be able to make a request again, if the oldest request we took into account falls outside the last minute window.
To help you predict this, you can find rate limiting specific headers on each response:
```
X-RateLimit-Limit: 200
# the number of requests we allow per sliding minute
X-RateLimit-Reset: 2021-06-15T10:51:23.035+0100
# the exact time your oldest significant request will expire;
# when over limit, this is the earliest time you'll be able to successfully make requests again
X-RateLimit-Remaining: 78
# the number of requests you have left before you will be denied access;
# note that this number can go up if earlier requests expire (after one minute)
```
You can use these headers to
- programmatically check the `X-RateLimit-Remaining` _number_ to avoid making calls that will be denied anyways, if the number is 0
- postpone execution until `X-RateLimit-Reset` _time_ to avoid waiting not long enough or too long before trying a next request
If you consistently bump into rate limits
- check if you can use `.list` endpoints instead of `.info` endpoints (did you know you can filter `.list` endpoints with a list of entity `ids`?)
- check if you can limit the items you fetch by making use of specific filters, eg. `updated_since`
- check if you can replace periodical checks with [webhooks](#/reference/other/webhooks)
- check if you can [side-load](#sideloading) related entities instead of fetching them yourself
- ask us for help through [api@teamleader.eu](mailto:api@teamleader.eu)
### Status codes
When objects are created, we return a `201` response containing the `id` and `type` of the new resource:
```json
{
"type": "contact",
"id": "9d4096c3-813f-4bd5-b3c4-4091807b5b74"
}
```
When objects are updated or actions are performed, we often return an empty response with a `204` status code.
Common used status codes:
- `200` - OK
- `201` - Created, when resources are created
- `204` - No Content, on resource updates or actions
- `400` - Bad Request, the request contains invalid data or references non-existing resources
- `401` - Unauthorized, invalid or missing access token
- `403` - Forbidden, not allowed to access this resource
- `404` - Not Found, resource not found
- `429` - Too Many Requests, your client has reached the API rate limit
- `500` - Internal Server Error, something went wrong on our end
### Collections
Collections are groups of items, represented as JSON arrays `[]`, such as:
* Line items for quotations, invoices
* Addresses for contacts, companies
* Customer tags
* Custom fields
#### Updating collections
Collections are replaced entirely during updates, so all the wanted values should be provided when updating entities.
Other existing values which are not provided will be removed.
```json
POST https://api.focus.teamleader.eu/contacts.update
{
"id": "f79a44dc-ec94-0d3c-b63e-8dcf41bba54e",
"tags": [
"existing tag",
"a new tag"
],
"custom_fields": [
{
"id": "0da89a10-794d-021d-9b2c-1f71dcd22489",
"value": "existing value"
},
{
"id": "fe50d0c5-49ba-0c12-8a34-d63becc7049e",
"value": "a new value"
}
]
}
```
> **Note:**
>
> + There is an exception for custom field collections where partial updates are possible, by including the parameter `custom_fields_update_strategy` with the value `partial` in the request body.
>
> + This will allow updating only the values of specific custom fields and leaving the remaining ones unchanged.
## Authentication
### OAuth 2
OAuth 2 is an authorization framework that allows a user to grant limited access to data in a Teamleader account, without having to expose their credentials.
To get access to the protected resources using our API, OAuth 2 uses access tokens. An access token is a string representing the granted permissions. Access tokens can be obtained after a user has completed the OAuth 2 authorization flow.
Before starting, you will need to register your integration (an OAuth 2 client) on our [Marketplace](https://marketplace.focus.teamleader.eu/build). Each registered integration is assigned a unique `client_id` and `client_secret`, which is used in the OAuth 2 authorization flow. Note that the `client_secret` key should not be shared or embedded in client-side code.
When you register an integration on our [Marketplace](https://marketplace.focus.teamleader.eu), it is required to select all *scopes* your integration wants access to.
For more detailed information about OAuth 2, we recommend reading [this article](https://auth0.com/docs/protocols/oauth2).
### Authorization flow
To get access to a user's Teamleader data using the *authorization code grant type*, redirect users to the Teamleader authorization page:
`https://focus.teamleader.eu/oauth2/authorize`
The required GET parameters are:
- `client_id` - issued when you create your integration
- `response_type` - must be "code"
- `state` - unique string to be passed back upon completion (optional)
- `redirect_uri` - URL to redirect back to after authorization
> A list of allowed redirect URIs needs to be configured on your integration's [settings page](https://marketplace.focus.teamleader.eu/build). Only redirect URIs matching one of these whitelisted URIs will be accepted. This is a security measure to prevent malicious users to impersonate your integration.
After logging in, the user will be asked to authorize your integration to access the data in their account. You will only be granted access to certain Teamleader data, based on the scopes you have selected on your integration's [settings page](https://marketplace.focus.teamleader.eu/build).
If the user authorizes your integration, they will be redirected to the specified `redirect_uri` with a temporary authorization code and the original `state` parameter. If the `state` parameter does not match the original value, the response may have been created by a third party and should be ignored.
`https://YOUR_REDIRECT_URI?code=CODE&state=STATE`
If the user denies your integration, he will be redirected to the `redirect_uri`, with an `error` parameter:
`https://YOUR_REDIRECT_URI?error=access_denied`
### Obtaining access tokens
After receiving the authorization code from the previous step, an access token can be requested to make API calls. Note that authorization codes can only be exchanged for an access token once and expire 10 minutes after issuance. To exchange the code for an access token, send an HTTP POST request to the following endpoint:
`https://focus.teamleader.eu/oauth2/access_token`
The required POST parameters are:
- `client_id` - issued when you register your integration
- `client_secret` - issued when you register your integration
- `code` - the authorization code
- `grant_type` - must be "authorization_code"
- `redirect_uri` - the original submitted redirect URL
It is recommended to add `content-type` header to this request. Supported content types are:
- application/x-www-form-urlencoded
- application/json
You will receive a JSON response containing an `access_token` and `refresh_token`:
```
{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "ACCESS_TOKEN",
"refresh_token": "REFRESH_TOKEN"
}
```
These access tokens are also known as bearer tokens. You can use this token to call API endpoints on behalf of the user, by adding it to the API request as an `Authorization` header.
```
POST https://api.focus.teamleader.eu/contacts.list HTTP/1.1
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciO...
Accept: application/json
```
Access tokens expire shortly (usually 1 hour) after they are issued for security reasons. If your integration needs to communicate with our API beyond the access token's lifespan, you will need to request a new access token using the refresh token which was issued with the access token. Note that refresh tokens can only be used once to get a new access token and refresh token.
### Using refresh tokens
If an access token is expired, a new access token and refresh token pair can be obtained by sending an HTTP POST request to the following endpoint:
`https://focus.teamleader.eu/oauth2/access_token`
The required POST parameters are:
- `client_id` - issued when you register your integration
- `client_secret` - issued when you register your integration
- `refresh_token` - the refresh token
- `grant_type` - must be "refresh_token"
Refresh tokens will continue functioning until the user revokes them or uninstalls your integration.
### Client-side authorization (implicit grant)
Your client secret must remain private and can not be embedded within client side applications such as browser plugins. For these types of applications, we have an alternative authorization method called using the [implicit grant](https://tools.ietf.org/html/rfc6749#section-1.3.2).
The implicit grant is a simplified authorization code flow optimized for clients implemented in a browser using a scripting language such as JavaScript. In the implicit flow, instead of issuing the client an authorization code, the client is issued an access token directly.
Similar to the regular flow, users are redirected to the Teamleader authorization page, but use `token` as the `response_type`:
`https://focus.teamleader.eu/oauth2/authorize`
The required GET parameters are:
- `client_id` - issued when you create your integration
- `response_type` - must be "token" (instead of "code")
- `state` - unique string to be passed back upon completion (optional)
- `redirect_uri` - URL to redirect back to after authorization
> A list of allowed redirect URIs needs to be configured on your integration's [settings page](https://marketplace.focus.teamleader.eu/build). Only redirect URIs matching one of these whitelisted URIs will be accepted. This is a security measure to prevent malicious users to impersonate your integration.
After authorization, the user is redirected to the `redirect_uri` with the following parameters in the **fragment part of the url**:
- `token_type` - with the value "Bearer"
- `expires_in` - the TTL for this access token in seconds
- `access_token` - the access token
- `state` - the state parameter sent in the original request
Example:
`https://YOUR_REDIRECT_URI#token_type=Bearer&expires_in=900&access_token=TOKEN&state=STATE`
Note that the implicit grant does not return refresh tokens and that our access tokens have a short TTL value. If you opt for this authorization method, you will have to go through the authorization flow every time you want to make API calls on behalf of the user. Keep this in mind while developing client-side applications.
### User Identity
To retrieve information about the user who authorized your application (the resource owner), call the `users.me` API endpoint:
`https://api.focus.teamleader.eu/users.me`
### PHP code example
You can find a (very) minimal PHP code example, showing how to start the OAuth 2 authentication flow and retrieve the user's identity using the obtained access token.
[https://github.com/teamleadercrm/api/blob/master/examples/oauth2.php](https://github.com/teamleadercrm/api/blob/master/examples/oauth2.php)
<a name="sideloading"></a>
## Sideloading
Sideloading (or compound documents) allows you to retrieve relevant related items as part of a single request. For example, a deal has a reference to a lead which can either be a contact or a company:
```
{
"data": {
"id": "f6871b06-6513-4750-b5e6-ff3503b5a029",
"title": "Deal with ACME",
"lead": {
"customer": {
"type": "company",
"id": "2659dc4d-444b-4ced-b51c-b87591f604d7"
}
}
}
}
```
Relationships are always represented by an object with a type/id combination. All of these relationships are candidates for sideloading.
To sideload relationships, use the dot-notation reference of the relationship's property and add it as an `include` parameter to your JSON body or your URL. In this case of our deal's customer, that would be `include=lead.customer`.
The sideloaded relationships are returned as part of an `included` property in the response body.
```
{
"data": {
"id": "f6871b06-6513-4750-b5e6-ff3503b5a029",
"title": "Deal with ACME",
"lead": {
"customer": {
"type": "company",
"id": "2659dc4d-444b-4ced-b51c-b87591f604d7"
}
}
},
"included": {
"company": [
{
"id": "2659dc4d-444b-4ced-b51c-b87591f604d7",
"name": "ACME"
}
]
}
}
```
Note that the sideloaded relationships are indexed based on their object type. Use the `type` property from your original relationship object to search for the sideloaded relationship in the `included` section.
Multiple relationships can be sideloaded at the same time by passing them as a comma-separated value such as `include=lead.customer,responsible_user`.
Sideloading is supported on every endpoint which returns objects.
## Changes & upgrades
We want to keep this API backwards compatible as long as possible, but at the same time, we need to make changes to it to match our evolving product.
Therefore we assigned a dated version to your API client when it was created.
This version controls the API and webhooks behaviour for your API client.
When we change the API in a backwards-incompatible way, we release a new dated version.
To avoid breaking your code, you stay on your assigned version until you [upgrade your API version](#upgrading-your-api-version).
### Types of changes
We consider the following changes to be backwards-compatible:
- Adding new API endpoints.
- Adding new optional parameters to existing API requests.
- Adding new properties to existing API responses.
- Adding new webhook types.
- Making existing parameters optional.
- Changing the order of properties in existing API responses.
- Fixing (potential) bugs that made an endpoint unusable (HTTP 500 errors).
- (Currently, we also hold ourselves the right to return a different error format, as there is still room for improvement)
We consider the following changes to be backwards-incompatible:
- Renaming or removing an endpoint.
- Renaming or removing a parameter of API requests.
- Renaming or removing a property of API responses.
- Changing the format of a parameter of API responses (eg. integer to float)
- Making validation of a parameter more strict (eg. making it required)
- Returning a different HTTP status code.
<a name="additions"></a>
### Additions
We list all backwards-compatible additions here. These are currently available in all published versions.
(There is also a [list of backwards-incompatible upgrades](#changelog) available, but those only apply if you [upgrade your API version](#upgrading-your-api-version).)
#### December 2024
- We added `initial_time_tracked`, `initial_price`, `initial_cost`, `initial_amount_billed`, and `initial_amount_paid` to `projects-v2/projects.create`. "Initial" values can be used when importing project data.
- We added `project` as a note type. `project` is a legacy project. For new projects, use `nextgenProject`.
#### November 2024
- We added `name` to `quotations.info` and `quotations.list`.
#### October 2024
- We added `preferred_currency` to the `companies.add` and `companies.update` endpoints.
- We added `payment_method` property to `invoice_generation` object on `subscription.info`, `subscriptions.create` and `subscriptions.update` endpoints.
#### September 2024
- We added `currency` to the `deals.create` and `deals.update` endpoints.
- We added `currency_exchange_rate` to the `deals.info` and `deals.list` endpoints.
- We added `pipeline` to the `deals.info` endpoint.
- The property `pipeline` on `deals.list` has been documented as resource.
- We added `will_be_automatically_switched_on` to the `accounts.projects-v2-status` endpoint.
#### August 2024
- We added the `accounts/projects-v2-status` endpoint to check the Projects version of an account.
- We added the `legacy_project` optional include to `projects-v2/projects.list` and `projects-v2/projects.info`.
- The `tickets.list` endpoint can now filter tickets by project ids.
- We added `billing_info` to `timeTracking.list` endpoint.
- We added the `users.getWeekSchedule` endpoint.
- We added `milestone_id` to the `tickets.create` and `tickets.update` endpoints.
- We added `milestone` and `project` to the `tickets.info` and `tickets.list` endpoints.
- We added `reference` to the `tickets.info` and `tickets.list` endpoints.
- We added `workOrder` to the `meetings.info` endpoint.
- We added `project_id` to the `tasks.update` endpoint.
- We added `work_order_id` to the `meetings.schedule` endpoint.
- We added `hourly_rate` to the `timeTracking.list` and `timeTracking.info` endpoints.
- We added `suppliers` as an optional include to the `products.info` endpoint.
#### July 2024
- We added `ticket` to the `tickets.getMessage` endpoint.
- We added `attachments` to the `emailTracking.list` endpoint.
- We added `attachments` to the `emailTracking.create` endpoint.
- We added `ticket.created`, `ticket.updated`, `ticket.closed`, `ticket.reopened` and `ticket.deleted` to supported Webhook types.
- We added `ticketMessage.added` to supported Webhook types.
- We added `custom_fields` as an optional include to `contacts.list`, `companies.list` and `deals.list` endpoints.
- We added `custom_fields` as an optional include to `projects-v2/projects.list` endpoint.
#### June 2024
- We added `tax` to the `products.info` endpoint.
- We added the `daysOff.import` and `daysOff.bulkDelete` endpoints.
- We added the `dayOffTypes.create`, `dayOffTypes.update` and `dayOffTypes.delete` endpoints.
- We added the `closingDays.add` and `closingDays.delete` endpoints.
#### May 2024
- We added `purchase_order_number` to `projects-v2/projects.info`, `projects-v2/projects.create` and `projects-v2/projects.update`.
- We added `product.updated` and `product.deleted` to supported Webhook types.
#### April 2024
- The meeting property `project` returned in `meetings.list` and `meetings.info` can now be an id of a `nextgenProject` as well.
#### February 2024
- We added the `tickets.importMessage`, `tickets.addReply` and `tickets.addInternalMessage` endpoints.
- We now support `ticket` as a valid `type` on the `cloudPlatforms.url` endpoint.
- We added the `initial_reply` to `tickets.create` endpoint.
- We added the `emailTracking.list` endpoint.
- We added `deal_id` to `calls.add` and `calls.update`.
- We added `deal` to `calls.list` and `calls.info`.
- We added `task.created`, `task.updated`, `task.completed`, `task.deleted` types to supported Webhook types.
- We added `contact.updatedLinkToCompany` to supported Webhook types.
- We added `includes` to the `companies.info` endpoint.
- We added `includes` to the `users.info` endpoint.
- We added `includes` to the `timeTracking.list` and `timeTracking.info` endpoints.
#### January 2024
- We renamed `task_type` and `task_type_id` to `work_type` and `work_type_id` in `projects-v2/tasks.list`, `projects-v2/tasks.info`, `projects-v2/tasks.create` and `projects-v2/tasks.update`. The old names are still supported, but deprecated.
- We replaced `task_type_rate` with `work_type_rate` in `projects-v2/tasks.list`, `projects-v2/tasks.info`, `projects-v2/tasks.create` and `projects-v2/tasks.update`. `task_type_rate` was never used.
- We added `project_id` to the `tasks.create` endpoint.
- We added the `users.listDaysOff` and `dayOffTypes.list` endpoints.
- We added `ticket` as a supported file subject on `files.list` and `files.upload`.
- We added `email_verification_status` to `users.me`.
- We added ticket endpoints `tickets.list`, `tickets.info`, `tickets.create` and `tickets.update`.
- We added endpoints for ticket messages `tickets.listMessages` and `tickets.getMessage`.
- We added the `ticketStatus.list` endpoint.
- We added `ticket_id` to `tasks.create`,`tasks.update`, `tasks.list` and `tasks.info`.
#### December 2023
- The `dealPhases.list` endpoint can now filter phases by pipeline id.
- We added `actions`, `requires_attention_after` and `probability` to `dealPhases.list`.
- We added the `dealPhases.create` endpoint.
- We added the `dealPhases.update` endpoint.
- We added the `dealPhases.duplicate` endpoint.
- We added the `dealPhases.move` endpoint.
- We added the `dealPhases.delete` endpoint.
- We added the `dealPipelines.list` endpoint.
- We added the `dealPipelines.create` endpoint.
- We added the `dealPipelines.update` endpoint.
- We added the `dealPipelines.markAsDefault` endpoint.
- We added the `dealPipelines.duplicate` endpoint.
- We added the `dealPipelines.delete` endpoint.
- The `deals.list` endpoint can now filter deals by pipeline ids.
- We added `pipeline` to `deals.list`.
- We now return the `default` payment term id in `paymentTerms.list` meta information.
- We added the `meetings.createReport` endpoint.
- We now return the user's team(s) in `users.info` and `users.list` endpoints.
- We added `nextgenProject` as a subject type to `files.upload`, `files.list` and `files.info`.
- The `/customFieldDefinitions.list` endpoint can now filter custom field definitions by context and ids.
- We added `nextgenProject` as a subject type to `emailTracking.create`.
- We added `nextgenProject` as a subject type to `notes.create` and `notes.list`.
- We added `nextgenProject` types to supported Webhook types.
- We added the `closingDays.list` endpoint.
#### November 2023
- We added the `calls.add`, `calls.list`, `calls.info` and `calls.complete` endpoints.
- We added the `callOutcomes.list` endpoint.
- We added `call.added`, `call.updated` and `call.deleted` types to supported Webhook types.
- We added the `meetings.schedule`, `meetings.update`, `meetings.complete` and `meetings.delete` endpoints.
- We added `meeting.created`, `meeting.updated` and `meeting.deleted` types to supported Webhook types.
- The `meetings.info` and `meetings.list` endpoints now return the meeting recurrence id for recurring meetings.
- The `meetings.list` endpoint can now filter meetings by recurrence id.
- We added `custom_fields` to `projects-v2/projects.info`, `projects-v2/projects.create` and `projects-v2/projects.update`
- We added `call.completed` and `meeting.completed` types to supported Webhook types.
- We added the `notes.list`, `notes.create` and `notes.update` endpoints.
- We added the `emailTracking.create` endpoint.
#### October 2023
- We added `late_fees` to `invoices.info` and `invoices.list`.
- We added the `invoices.updateBooked` endpoint.
#### September 2023
- We added the `meetings.list` and `meetings.info` endpoints.
- We added `time_estimated` and `amount_unbilled` to `projects-v2/projects.list`.
- We added sorting on `amount_billed`, `amount_paid`, `amount_unbilled`, `external_budget_spent`, `external_budget`, `internal_budget` and `time_estimated` to `projects-v2/projects.list`.
#### August 2023
- We added `quotation` as a type for `cloudPlatforms.url`.
- We added `currency` to `invoices.info`, `subscriptions.info`, `quotations.info` and `creditNotes.info`.
- We added `product_category` to `products.info`.
- We added the `invoices.creditPartially` endpoint.
- We added a `paymentMethods.list` endpoint.
- We added `payment_method_id` to `invoices.registerPayment`.
- We added the `cloudPlatforms.url` endpoint.
- We added `expiry` to `quotations.create`, `quotations.update`, `quotations.info` and `quotations.list`.
- We added `expired` as possible value for a quotation status.
- We added `subscription.add`, `subscription.updated`, `subscription.deleted`, `subscription.deactivated` types to supported Webhook types
#### July 2023
- We added `status` to the filters on `contacts.list`.
- We added `status` to the filters on `companies.list`.
- We added a `mailTemplates.list` endpoint.
- We added a `unitsOfMeasure.list` endpoint.
- We added `unit` on `grouped_lines` to `invoices.info`, `subscriptions.info`, `quotations.info` and `creditNotes.info`
- We added `unit_of_measure_id` on `grouped_lines` to `invoices.draft`, `invoices.update`, `subscriptions.create`, `subscriptions.update`, `quotations.create` and `quotations.update`.
- We added the `invoices.send` endpoint.
- 🎉 We added all endpoints for the _New Projects_ module: `projects-v2/projects`, `projects-v2/projectLines`, `projects-v2/projectGroups`, `projects-v2/tasks`, and `projects-v2/materials`. 🎉
- We added `billing_info` on `timetrackings.info`.
- We added `invoice_number` to the filters on `invoices.list`.
- We added the `priceLists.list` endpoint.
- We added `price_list_prices` to `products.info` and `products.add`.
- We added `stock` and `configuration` to `products.list`, `products.info` and `products.add`.
- We added `department_id`, `product_category_id` and `tax_rate_id` to `products.add`.
- We added the `products.update` endpoint.
- We added the `products.delete` endpoint.
- We added `added_at` to `products.info`.
- We added `updated_at` to `products.info` and `products.list`.
- We added `custom_fields` to `products.add` and `products.update`.
- We added `selling_price` to `products.update`.
- We added `unit_of_measure_id` to `products.add` and `products.update`.
- We added `unit` to `products.info` and `products.list`.
- We added the `currencies.exchangeRates` endpoint.
- We added `document_template` to `subscriptions.info`.
- We added `document_template_id` to `subscriptions.create` and `subscriptions.update`.
- We added `deal` as a link type to `events.create` and `events.update`.
#### June 2023
- We enhanced the existing filter on `term` in `users.list`.
In addition to returning the users who have the term in their first name, last name or email address, we now also return those users who have the term in their function.
- We added the `commercialDiscounts.list` endpoint.
- We added the `documentTemplates.list` endpoint.
#### May 2023
- We added `team_lead_id` to the filters on `teams.list`.
- We added sorting on `first_name`, `last_name`, `email` and `function` to `users.list`.
- We added `creditNote.updated`, `creditNote.deleted`, `creditNote.sent` types to supported Webhook types.
#### April 2023
- We added `ids` to the filters on `teams.list`.
#### March 2023
- We added `customField`,`timeTracking` and `department` as allowed types on `migrate.id`.
- We added `credit_note_date_before` and `credit_note_date_after` to the filters on `creditNotes.list`.
- We added `ids` to the filters on `timeTrackings.list`.
- We added `term` to the filters on `teams.list`.
- The property `gender` has been expanded with additional values on:
- `contacts.create`
- `contacts.info`
- `contacts.list`
- `contacts.update`
#### February 2023
- Product custom fields are now included as `custom_fields` on `products.info`.
#### January 2023
- We added `user_id` to `timeTracking.add`.
- We updated the HTTP verb for all endpoints to `POST`. Using `GET ` for `.list`, `.info` and `migrate` endpoints is still possible, but deprecated.
#### December 2022
- We added `secondary_position` and `division` to the related company data on `contacts.info`.
#### November 2022
- We added `invoice_date` as a sort field on `invoices.list`.
#### October 2022
- We added `default_department` and `created_at` to the sorting options on `departments.list`.
#### September 2022
- We added `project_id` to `invoices.draft` and `invoices.update`.
- We added `payment_term`, `grouped_lines` and `invoice_generation` to `subscriptions.info`.
- We added `starts_on` and `ends_on` to `subscriptions.list` and `subscriptions.info`.
- We added `subscriptions.create` and `subscriptions.update`.
- We added the `quotations.send` endpoint.
- We clarified how the optional `folder` argument for the `files.upload` endpoint works.
#### August 2022
- We added `invoices.removePayments`.
- We added `department_id` to the filters on `subscriptions.list`.
- We added `tags` to `companies.update`.
#### July 2022
- We added `product` and `project` as subject type for `files.list`.
- We added `priority` to `tasks.list` and `tasks.info`.
- We added `vat_number` to the filters on `companies.list`.
#### June 2022
- We added `files.list`, `files.info`, `files.download`, `files.upload` and `files.delete`.
#### May 2022
- We added `status` as a sort field on `subscriptions.list`.
#### April 2022
- We added the `contacts.updateCompanyLink` endpoint.
- We added the `subscriptions.deactivate` endpoint.
#### March 2022
- We added the `subscriptions.info` and `subscriptions.list` endpoints.
#### February 2022
- We added `customer` to the filters on `invoices.list`.
- We added a `subscription` filter to `invoices.list`.
- We added `project` to `invoices.list` and `invoices.info`.
#### December 2021
- We added `on_hold_since` to `invoices.info`.
- We added `deal_id` to the filters on `invoices.list`.
- We added `project_id` to the filters on `invoices.list`.
- We added `invoice_date_before` and `invoice_date_after` to the filters on `invoices.list`.
- We added an `updated_since` filter to `projects.list`.
#### November 2021
- We added `status` on `companies.list` and `companies.info`.
- We added `status` on `contacts.list` and `contacts.info`.
- We added `ids` to the filters on `creditNotes.list`.
- We added `invoice_id` and `project_id` to the filters on `creditNotes.list`.
- We added `discounts` to `creditNotes.info`.
- We added `added_at` on `products.list`, and `tasks.list`.
- We added `created_at` on `projects.list`.
#### September 2021
- We added `sort` to `timeTracking.list`.
- We added `teams.list`.
#### June 2021
- We added `description` on `products.add`.
- We added `title` on `tasks.info` and `tasks.list`.
- We added `term` to the filters on `workTypes.list`.
- We added `started_on` to `timeTracking.update` and `timeTracking.add`.
- We added the `quotations.delete` endpoint.
- We changed the behaviour of the started_before, started_after, ended_before, and ended_after filters on timeTracking.list to be inclusive. So "started after midnight" will include time tracking that started exactly on midnight.
- We added `status` to `quotations.info` and `quotations.list`.
#### March 2021
- Updated the `migrate.id` endpoint for type `quotation` to return the first or accepted quotation uuid for given deal id.
#### January 2021
- We added `deal` on `invoices.list` and `invoices.info`.
#### October 2020
- We added `title` as a sort field on `projects.list`.
- We added `area_level_two` to the .list and .info responses for `company`, `contact` and `department` addresses.
- We added `area_level_two_id` to the .add and .update calls for `companies` and `contacts`.
- We added `starts_on` to `milestones.create`, `milestones.update`, `milestones.list`, `milestones.info`, and `projects.create` (in the milestone context).
- We added `budget` on `projects.update`.
- We added `customer` on `projects.update`.
- We added `starts_on` and `desc` to the sort options on `milestones.list`.
- We added `file` to `invoices.list` and `invoices.info`.
- We added `quotations.accept` endpoint.
#### September 2020
- We added the `milestones.close` and `milestones.open` endpoints.
- We added the `created_at` and `updated_at` fields to `quotations.list` and `quotations.info`. Please note that these fields are nullable and will only be filled in for future quotations.
- We added the `discounts` field to `quotations.info`.
- We added the `purchase_price` field to `quotations.list`, `quotations.info`, `quotations.create` and `quotations.update`.
- We added `projects.close` and `projects.reopen` endpoints.
#### August 2020
- We added a filter `done` to `events.list`.
- We added the `currency_exchange_rate` to `invoices.list`.
- We added the `product_id` property to `quotations.create`, `quotations.update`, `invoices.draft` and `invoices.update`.
#### July 2020
- We added `quotations.list`, `quotations.create` and `quotations.update`.
#### June 2020
- We added the `emails` property to `departments.list`.
- We added the `status` property to `departments.info` and `departments.list`, this property is also available as a filter on `departments.list`.
- We added a filter `scheduled` to `tasks.list`.
- We added the `total` field to `quotations.info`.
#### May 2020
- We no longer require all fields in `projects.update`, only the id remains required.
- We added the `purchase_order_number` field to `projects.info`, `projects.create`, and `projects.update`.
#### April 2020
- We added the `description` field to `milestones.info`, `milestones.create`, and `milestones.update`.
- We added the `propagate_date_changes` property to `milestones.update`. This allows propagating the new due date difference to the due date of all open tasks of that milestone, and recursively to all depending milestones.
- We added the `vat_number` property to `contacts.info`.
- Project custom fields are now included as `custom_fields` on `projects.info`. They can also be added and updated through the `projects.create` / `projects.update` endpoints.
#### March 2020
- We added actuals and budget to `projects.list`, `projects.info`, `milestones.list`, and `milestones.info`.
- We added `depends_on` to the `milestones.info` and `milestones.list` endpoints.
- We added `dependency_for` to the `milestones.info` and `milestones.list` endpoints. This indicates all the milestones for which the current milestone is a dependency.
- We added `depends_on` to the `milestones.create` and `milestones.update` endpoints. It allows setting a dependency on another milestone of the same project.
#### February 2020
- We added an `ids` filter to `lostReasons.list`.
- As events can be linked to deals, we added the `deal` link type to `events.list` and `events.info`, and allow filtering on deals in `events.list`.
- We added `lost_reason` to the response of `deals.info`.
- We added `lost_reason` to the response of `deals.list`. This is composed of a `lostReason` object along with a more elaborate `remark`.
- We added a filter `term` to `invoices.list`.
- We added the `purchase_order_number` to the response of both `deals.list` and `deals.info` request.
- We added the `summary` to the response of both the `deals.list` and `deals.info` request. The `summary` can also be added and updated through the `deals.create` / `deals.update` endpoints.
#### January 2020
- We added the time tracking `locked` and `updatable` properties to `timeTracking.info`.
- We exposed the "Freeze time tracking after a number of days" preference in `users.me`.
- We added format `ubl/e-fff` to `invoices.download`.
- We added filters `estimated_closing_date_from` and `estimated_closing_date_until` to `deals.list`.
- We added a new sorting option `weighted_value` to `deals.list` and will allow ascending as well as descending orders on sorts.
The default remains created_at and descending as before if nothing is passed.
- We added the `weighted_value` to the response of both `deals.list` and `deals.info` requests.
- We added the `extra_option_allowed` information (for single select custom fields) to `customFieldDefinitions.list` and `customFieldDefinitions.info` endpoints.
- We added format `ubl/e-fff` to `creditNotes.download`.
#### October 2019
- We added `milestones.delete`.
- We added a `estimated_closing_date` filter to `deals.list`.
- We added a filter `created_before` to `deal.list`.
- We modified filter `responsible_user_id` in `deal.list` to also accept an array of responsible_user_ids.
- Task custom fields are now included as `custom_fields` on `tasks.info`. They can also be added and updated through the `tasks.create` / `tasks.update` endpoints.
- Milestone custom fields are now included as `custom_fields` on `milestones.info`. They can also be added and updated through the `milestones.create` / `milestones.update` endpoints.
#### July 2019
- We added an `updated_since` filter to `products.list`.
- We added `project` to `tasks.info` and `tasks.list`.
- We added a filter by `subject` to `timeTracking.list`.
- A `task_id` filter has been added to `/events.list`.
- We added a `term` filter to `milestone.list`.
#### June 2019
- We added a `term` filter to `tasks.list`.
- We added `tasks.complete` and `tasks.reopen` endpoints.
- We added `due_by` and `due_from` to `tasks.list` filters.
- We added a `tasks.schedule` endpoint.
- We added the ability to sort `tasks.list` based on a descending `due_at`.
- `tasks.info` and `tasks.list` now return information about the customer.
- We added `customer` parameter to `tasks.create` and `tasks.update`.
- In order to link milestones and deals to a task, we added `milestone_id` and `deal_id` as optional parameters to `tasks.create` and `tasks.update`.
- `tasks.list` and `tasks.info` now return the milestone and deal objects.
#### May 2019
- We added a `started_at` property to `timers.update` in order to be able to update the start time of a running timer.
- We added `sent` as a boolean to `invoices.info` and `invoices.list`.
- We added an optional filter on `ids` to `invoices.list`.
- We added `creditNote.booked` and `product.added` to the available webhooks list.
#### April 2019
- In `users.info`, we added the `status` property to the response since it is now possible to retrieve deactivated users.
- In `users.list`, you can now also request deactivated users, by using a new filter on `status`.
By default, the endpoint keeps returning active users only. The status is also returned in the response data.
- We've added a `products.add` endpoint.
Because you can provide the selling price of a product in this endpoint, we also return it in `products.info` from now on.
- We now also return the `payment_reference` of an invoice in the `invoices.list`.
We also made it filterable through the `payment_reference` filter.
- We added the `purchase_order_number` to all relevant endpoints (`invoices.draft`, `invoices.update`, `invoices.info` and `invoices.list`).
The property is also filterable through the `purchase_order_number` filter.
- We added `currency_exchange_rate` to `quotations.info`.
- The `events.list` endpoint now has an optional filter property `ids`.
- The `tasks.list` endpoint now has an optional filter property `ids`.
- We added an `invoices.credit` endpoint.
#### March 2019
- On `invoices.draft` and `invoices.update` you can now also provide (a) global discount(s) through the `discounts` property.
- We've published a bunch of `tasks` endpoints (`tasks.list`, `tasks.info`, `tasks.create`, `tasks.update`, `tasks.delete`).
- We now also return a `currency_exchange_rate` on all `.info` endpoints where we support multi-currency.
That is `quotations.info`, `invoices.info` and `creditNotes.info`.
#### February 2019
- We now also return the `invoicing_method` of a milestone. Both in `milestones.list` and `milestones.info`.
- We now expose the invoiceability of time trackings and running timers. You can now use the `invoiceable` property on the following endpoints:
- `timeTracking.list`
- `timeTracking.info`
- `timeTracking.add`
- `timeTracking.update`
- `timers.current`
- `timers.start`
- `timers.update`
We also return an `invoiceable` preference in the `users.me` endpoint that exposes whether a user prefers to track time by default as invoiceable or not.
- We've added a `national_identification_number` property on contacts.
You can read it from `contacts.list` and `contacts.info`.
It's also writeable through `contacts.add` and `contacts.update`.
- Invoicing custom fields included as `custom_fields`.
They are returned in `invoices.info`.
They can be added or updated through `invoices.draft` and `invoices.update`.
- Deal can now be filtered with a `term` in `deals.list`.
We will return deals of which the term is part of the title, reference or customer's name.
- We have created a way to fetch more data about related resources: sideloading.
More information can be found [here](#sideloading).
- We didn't provide you with a way to find the uuid of a quotation.
Based on a v1 deal id, you can now retrieve the uuid of a quotation with `migrate.id`.
#### January 2019
- The `projects.list` endpoint has an optional filter to filter on `term` now.
We return projects for which the term appears in the title or description.
- During `invoices.draft` and `invoices.update`, we now allow you to provide a `withholding_tax_rate_id`.
Don't worry if you don't know what this is. Withholding taxes only apply in certain countries, eg Spain or Italy.
- We added a filter on `term` in `users.list`.
We return those users who have the term in their first name, last name or email address.
- We already had a part of the products APIs for our own usage, but since it might be useful to you, we are making it public.
So `products.list` and `products.info` are now available for usage.
<a name="upgrading-your-api-version"></a>
### Upgrading your API version
This documentation only reflects the latest version of the API.
If you need to make changes to your client or you want to make use of the latest feature, you might need to upgrade your API version.
You can find your current API version, in the `X-Api-Version` header on any API response.
The current latest version is **2023-09-26**.
After checking the API [changelog](#changelog) to see which endpoints work differently, you can upgrade your API version:
Send an `X-Api-Version` header containing the identifier of a version (newer than your current version) with your requests.
_This will only affect the version for those API calls and won't affect any other calls done by your client._
<a name="changelog"></a>
### Changelog
We list all backwards-incompatible changes here. As described above, new additions and forwards-compatible changes don’t need a new API version and can be found [here](#additions).
#### 2023-10-01
As of October 1st 2023, we expect all network clients to make use of TLS 1.2 or higher when connecting to our infrastructure.
#### 2023-09-26
- When updating events through `events.update`, links to deals must also be provided otherwise existing deal link(s) will be removed.
- `unit_price.currency` is no longer returned for line items on the response of `invoices.info`, `subscriptions.info`, `quotations.info` and `creditNotes.info`.