├── .gitignore ├── sections ├── samples │ ├── get.py │ ├── file_upload.py │ ├── post.py │ ├── get.php │ ├── get.js │ ├── get.gs │ ├── file_upload.js │ ├── post.php │ ├── post.js │ ├── file_upload.php │ └── post.gs ├── sample_code.md ├── datetime.md ├── content_types.md ├── sessions.md ├── authentication.md ├── filtering.md ├── currencies.md ├── workflows.md ├── users_tasks.md ├── bookings.md ├── workflow_statuses.md ├── invoice_payments.md ├── tasklists.md ├── milestones.md ├── discussions.md ├── subtasks.md ├── company.md ├── clients.md ├── client_contacts.md ├── expenses.md ├── comments.md ├── entries.md ├── project_statuses.md ├── files.md ├── task_recurring_profiles.md ├── includes.md ├── webhooks.md ├── users.md ├── tasks.md ├── project_templates.md └── estimate_templates.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | node_modules -------------------------------------------------------------------------------- /sections/samples/get.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | email = 'johndoe@email.com' 4 | password = 'secret' 5 | 6 | headers = {'Accept': 'application/json'} 7 | r = requests.get('https://app.paymoapp.com/api/projects', headers=headers, auth=(email, password)) 8 | 9 | # List project names 10 | for project in r.json()['projects']: 11 | print project['name'] 12 | -------------------------------------------------------------------------------- /sections/samples/file_upload.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | email = 'johndoe@email.com' 4 | password = 'secret' 5 | 6 | payload = {'project_id': 123456} 7 | headers = {'Accept': 'application/json'} 8 | r = requests.post('https://app.paymoapp.com/api/files', 9 | data=payload, 10 | files={'file': open('/path/to/file.jpg', 'rb')}, 11 | headers=headers, 12 | auth=(email, password)) 13 | 14 | print 'New File URL: %s' % r.json()['files'][0]['file'] 15 | -------------------------------------------------------------------------------- /sections/samples/post.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | email = 'johndoe@email.com' 4 | password = 'secret' 5 | 6 | payload = { 7 | 'client_id': 123456, 8 | 'name': 'New Project', 9 | 'description': 'Project added from API' 10 | } 11 | headers = {'Accept': 'application/json'} 12 | r = requests.post('https://app.paymoapp.com/api/projects', json=payload, headers=headers, auth=(email, password)) 13 | 14 | print 'New Project ID: %s' % r.json()['projects'][0]['id'] 15 | -------------------------------------------------------------------------------- /sections/samples/get.php: -------------------------------------------------------------------------------- 1 | =1583954441 17 | -------------------------------------------------------------------------------- /sections/samples/post.php: -------------------------------------------------------------------------------- 1 | 123456, 8 | "name" => "New Project", 9 | "description" => "Project added from API" 10 | ); 11 | 12 | $ch = curl_init(); 13 | curl_setopt($ch, CURLOPT_URL, $target_url); 14 | curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept: application/json")); 15 | curl_setopt($ch, CURLOPT_USERPWD, $email . ":" . $password); 16 | curl_setopt($ch, CURLOPT_POST, 1); 17 | curl_setopt($ch, CURLOPT_POSTFIELDS, $post); 18 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 19 | 20 | $result = curl_exec($ch); 21 | if ($result === false) { 22 | echo "Curl error: " . curl_error($ch) . "\n"; 23 | } 24 | curl_close($ch); 25 | 26 | echo "New project ID: " . json_decode($result, true)['projects'][0]['id']; 27 | -------------------------------------------------------------------------------- /sections/samples/post.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const email = 'johndoe@email.com'; 3 | const password = 'secret'; 4 | 5 | var postData = { 6 | client_id: 123456, 7 | name: 'New Project', 8 | description: 'Project added from API' 9 | }; 10 | 11 | request.post( 12 | { 13 | url: 'https://app.paymoapp.com/api/projects', 14 | body: JSON.stringify(postData), 15 | headers: { 16 | 'Content-type': 'application/json', 17 | 'Accept': 'application/json' 18 | }, 19 | auth: { 20 | user: email, 21 | pass: password 22 | } 23 | }, 24 | function (error, response, body) { 25 | if (!error) { 26 | console.log('New project ID: ' + JSON.parse(body).projects[0].id); 27 | } else { 28 | console.log(error); 29 | } 30 | } 31 | ); 32 | -------------------------------------------------------------------------------- /sections/samples/file_upload.php: -------------------------------------------------------------------------------- 1 | 123456, 10 | "file" => new CURLFile( $file_name_with_full_path ) 11 | ); 12 | 13 | $ch = curl_init(); 14 | curl_setopt($ch, CURLOPT_URL, $target_url); 15 | curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept:application/json")); 16 | curl_setopt($ch, CURLOPT_USERPWD, $email . ":" . $password); 17 | curl_setopt($ch, CURLOPT_POST, 1); 18 | curl_setopt($ch, CURLOPT_POSTFIELDS, $post); 19 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 20 | $result = curl_exec($ch); 21 | 22 | if ($result === false) { 23 | echo "Curl error: " . curl_error($ch) . "\n"; 24 | } 25 | 26 | curl_close($ch); 27 | echo "New File URL: " . json_decode($result, true)['files'][0]['file']; 28 | -------------------------------------------------------------------------------- /sections/samples/post.gs: -------------------------------------------------------------------------------- 1 | function samplePostRequest() { 2 | const email = 'johndoe@email.com'; 3 | const password = 'secret'; 4 | 5 | var headers = { 6 | "Authorization": "Basic " + Utilities.base64Encode(email + ":" + password), 7 | "Accept": "application/json", 8 | "Content-type": "application/json" 9 | }; 10 | 11 | var postData = { 12 | client_id: 123456, 13 | name: 'New Project', 14 | description: 'Project added from API' 15 | }; 16 | 17 | var params = { 18 | "method": "POST", 19 | "headers": headers, 20 | "payload": JSON.stringify(postData) 21 | }; 22 | 23 | var url = "https://app.paymoapp.com/api/projects"; 24 | var response = UrlFetchApp.fetch(url, params); 25 | 26 | Logger.log("response code: " + response.getResponseCode()); 27 | 28 | var responseJson = JSON.parse(response.getContentText()); 29 | Logger.log("New Project ID: " + responseJson.projects[0].id); 30 | } 31 | -------------------------------------------------------------------------------- /sections/content_types.md: -------------------------------------------------------------------------------- 1 | # Content types 2 | 3 | ## Request content type 4 | 5 | **All POST and PUT requests should send the `Content-Type` header** with the appropriate value. 6 | 7 | Supported types: 8 | 9 | ### application/json 10 | 11 | Example: 12 | 13 | ```shell 14 | curl -u email:password 15 | -H 'Content-Type: application/json' 16 | -d '{"name": "new name"}' 17 | https://app.paymoapp.com/api/projects/12345 18 | ``` 19 | 20 | ### text/xml 21 | 22 | In case of XML requests, the params should be wrapped inside a `` tag. 23 | 24 | Example: 25 | 26 | ```shell 27 | curl -u email:password 28 | -H 'Content-Type: text/xml' 29 | -d 'new name' 30 | https://app.paymoapp.com/api/projects/12345 31 | ``` 32 | 33 | ### application/x-www-form-urlencoded 34 | 35 | Example: 36 | 37 | ```shell 38 | curl -u email:password 39 | -H 'Content-Type: application/x-www-form-urlencoded' 40 | -d 'name=new%20name' 41 | https://app.paymoapp.com/api/projects/12345 42 | ``` 43 | 44 | ### multipart/form-data 45 | 46 | This content type is used when uploading files. 47 | 48 | ## Response content type 49 | 50 | The format of the response is specified by the `Accept` header. 51 | 52 | The options are: 53 | 54 | * `Accept: application/json` for getting the response in JSON format. 55 | * `Accept: text/xml` for XML format. 56 | 57 | If `Accept` header is not specified, the response will be in JSON format by default. 58 | 59 | Additionally, for reports, invoices, estimates, expenses there is a PDF version of the document. You can get it by adding a `format=pdf` param to the query string. 60 | 61 | For reports, adding `format=xlsx` will return the report in MS Excel format. 62 | 63 | -------------------------------------------------------------------------------- /sections/sessions.md: -------------------------------------------------------------------------------- 1 | # Sessions 2 | 3 | * [Getting sessions](#list) 4 | * [Getting a session](#get) 5 | * [Creating a session](#create) 6 | * [Deleting a session](#delete) 7 | 8 | Sessions are used for authentication as described in [Authentication Guide](authentication.md#sessions). 9 | 10 | 11 | ## Getting sessions 12 | 13 | You can list sessions for the current user by making a GET request to: 14 | 15 | * `/api/sessions` 16 | 17 | Example of response: 18 | 19 | ```json 20 | { 21 | "sessions": [ 22 | { 23 | "id": "1762c607ec2b3e9e13c323cbafeb7ecd", 24 | "ip": "10.0.2.2", 25 | "expires_on": "2015-02-16T13:54:41Z", 26 | "created_on": "2015-01-16T13:54:41Z", 27 | "updated_on": "2015-01-16T13:54:41Z", 28 | "user_id": 1 29 | }, 30 | { 31 | "id": "13ad2ecdb6d59343fa25214c35e9430e", 32 | "ip": "212.93.154.90", 33 | "expires_on": "2015-02-07T10:03:33Z", 34 | "created_on": "2015-01-07T10:03:33Z", 35 | "updated_on": "2015-01-07T10:03:33Z", 36 | "user_id": 1 37 | } 38 | ] 39 | } 40 | ``` 41 | 42 | 43 | ## Getting a session 44 | 45 | To get the session info, make a GET request to: 46 | 47 | * `/api/sessions/[SESSION_ID]` 48 | 49 | Example response: 50 | 51 | ```json 52 | { 53 | "sessions": [ 54 | { 55 | "id": "1762c607ec2b3e9e13c323cbafeb7ecd", 56 | "ip": "10.0.2.2", 57 | "expires_on": "2015-02-16T13:54:41Z", 58 | "created_on": "2015-01-16T13:54:41Z", 59 | "updated_on": "2015-01-16T13:54:41Z", 60 | "user_id": 1 61 | } 62 | ] 63 | } 64 | ``` 65 | 66 | 67 | ## Creating a session 68 | 69 | To create a session, make a POST request with an empty body to: 70 | 71 | * `/api/sessions` 72 | 73 | The response will contain the new session info. 74 | 75 | 76 | ## Ending a session 77 | 78 | To end a session, send a DELETE request to: 79 | 80 | * `/api/sessions/[SESSION_ID]` 81 | 82 | If successful, the response will have a `200 OK` status code. -------------------------------------------------------------------------------- /sections/authentication.md: -------------------------------------------------------------------------------- 1 | # Paymo API Authentication 2 | 3 | ## Basic Auth 4 | 5 | For a quick start using the Paymo API, you can use [HTTP Basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) with your email and password info: 6 | 7 | ```shell 8 | curl -u email:password 9 | -H 'Accept: application/json' 10 | https://app.paymoapp.com/api/clients 11 | ``` 12 | 13 | Most HTTP client applications support HTTP Basic authentication out of the box. 14 | 15 | 16 | ## API Keys 17 | 18 | When using Paymo API with a third-party software, we recommend you using API Keys instead of asking for user's email/password. 19 | 20 | API Keys can be generated from the Paymo application, on the [My Account](https://app.paymoapp.com/#Paymo.module.myaccount/) page. 21 | 22 | Using an API Key is similar to Basic Auth, but instead of providing the email/password, you provide the API Key as the username and any text for the password. 23 | 24 | For example: 25 | 26 | ```shell 27 | curl -u YOUR_API_KEY:SOME_RANDOM_TEXT 28 | -H 'Accept: application/json' 29 | https://app.paymoapp.com/api/me 30 | ``` 31 | 32 | 33 | ## Using Sessions 34 | 35 | Another way to authenticate the API requests are by using session tokens. This token is sent by an HTTP header named `X-Session` 36 | 37 | ```shell 38 | curl -H 'X-Session: abcdef01234567890' 39 | -H 'Accept: application/json' 40 | https://app.paymoapp.com/api/clients 41 | ``` 42 | 43 | Anyone using the session token has the same access level as the user for whom the session token was created. 44 | 45 | ### Creating session tokens 46 | 47 | Session tokens are creating by making a POST request to 48 | 49 | * `https://app.paymoapp.com/api/sessions` 50 | 51 | When making this request, you have to use HTTP Basic authentication or an API Key. 52 | 53 | Sample request: 54 | 55 | ```shell 56 | curl -u email:password 57 | -H 'Accept: application/json' 58 | https://app.paymoapp.com/api/sessions 59 | ``` 60 | 61 | Sample response: 62 | ```json 63 | { 64 | "sessions": [ 65 | { 66 | "id": "9762c607ec2b5d9e13c423cbafeb7ec8", 67 | "ip": "10.0.2.2", 68 | "expires_on": "2015-02-16T13:54:41Z", 69 | "created_on": "2015-01-16T13:54:41Z", 70 | "updated_on": "2015-01-16T13:54:41Z", 71 | "user_id": 1234 72 | } 73 | ] 74 | } 75 | ``` 76 | 77 | A session token has an expiration date, after which it will be no longer valid. 78 | 79 | ### Ending a session 80 | 81 | When you want to end a session and make its token invalid, make a DELETE request to: 82 | 83 | * `https://app.paymoapp.com/api/sessions/[SessionID]` 84 | 85 | -------------------------------------------------------------------------------- /sections/filtering.md: -------------------------------------------------------------------------------- 1 | # Filtering 2 | 3 | If you make a GET request to retrieve a list of objects but you want the response to contain only those items that match a certain criteria, you should use the `where` param in the query string. 4 | 5 | Let's say want to get a list of tasks that are not yet marked as complete. 6 | 7 | The request would be: 8 | 9 | ```shell 10 | curl -u email:password 11 | -H 'Accept: application/json' 12 | https://app.paymoapp.com/api/tasks?where=complete=false 13 | ``` 14 | 15 | And the response looks like: 16 | 17 | ```json 18 | { 19 | "tasks": [ 20 | { 21 | "id": 241147, 22 | "name": "Logo", 23 | "project_id": 28917, 24 | "tasklist_id": 59917, 25 | "user_id": null, 26 | "complete": false, 27 | "billable": true, 28 | "seq": 1, 29 | "description": "", 30 | "price_per_hour": null, 31 | "due_date": "2013-09-25", 32 | "budget_hours": 80, 33 | "users": [ 34 | 1563 35 | ], 36 | "created_on": "2013-06-26T12:07:44Z", 37 | "updated_on": "2014-07-23T14:22:05Z" 38 | }, 39 | { 40 | "id": 241148, 41 | "name": "HTML coding", 42 | "project_id": 28917, 43 | "tasklist_id": 59918, 44 | "user_id": null, 45 | "complete": false, 46 | "billable": true, 47 | "seq": 4, 48 | "description": "", 49 | "price_per_hour": null, 50 | "due_date": "2013-09-11", 51 | "budget_hours": 60, 52 | "users": [ 53 | 1570 54 | ], 55 | "created_on": "2013-06-26T12:07:44Z", 56 | "updated_on": "2014-07-23T14:22:05Z" 57 | } 58 | ] 59 | } 60 | ``` 61 | 62 | You can add multiple conditions in the `where` param by using the `and` operator. As in the example below: 63 | 64 | ```shell 65 | curl -u email:password 66 | -H 'Accept: application/json' 67 | https://app.paymoapp.com/api/tasks?where=complete=false and billable=true 68 | ``` 69 | 70 | **Note**: the value of the `where` param will be urlencoded, but in the examples here we'll not do that to keep the text clear. 71 | 72 | In the example above, the URL should look like this: `https://app.paymoapp.com/api/tasks?where=complete%3Dfalse%20and%20billable%3Dtrue` 73 | 74 | ## Available operators 75 | 76 | Operators used in `where` condition are: `=`, `>`, `>=`, `<`, `<=`, `!=`, `like`, `not like`, `in (value1,value2,...)`, `not in (value1, value2)` 77 | 78 | Examples of `where` conditions (not urlencoded for clarity): 79 | 80 | * `client_id in (123,124)` 81 | * `name like Sample` 82 | * `name="Sample Project"` 83 | * `price_per_hour>50` 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /sections/currencies.md: -------------------------------------------------------------------------------- 1 | # Currencies 2 | 3 | List of available currencies in Paymo: 4 | 5 | Code | Symbol | Description 6 | ---- | ------ | ----------- 7 | AED | AED | United Arab Emirates dirham 8 | AMD | AMD | Armenian dram 9 | ANG | ƒ | Netherlands Antillean guilder 10 | AOA | Kz | Angolan kwanza 11 | ARS | $ | Argentine peso 12 | AUD | $ | Australian dollar 13 | AWG | Afl | Aruban guilder 14 | BAM | KM | Bosnia and Herzegovina convertible mark 15 | BDT | ৳ | Bangladesh Taka 16 | BGN | лв | Bulgarian lev 17 | BRL | R$ | Brazilian real 18 | BWP | P | Botswana pula 19 | CAD | $ | Canadian dollar 20 | CFP | XPF | Pacific Franc Exchange 21 | CHF | CHF | Swiss franc 22 | CLF | UF | Unidad de Fomento 23 | CLP | $ | Chilean pesos 24 | CNY | ¥ | Chinese yuan 25 | COP | $ | Colombian peso 26 | CRC | ₡ | Costa Rican colón 27 | CZK | Kč | Czech koruna 28 | DKK | kr | Danish krone 29 | DOP | RD$ | Dominican peso 30 | DZD | د.ج | Algerian dinar 31 | EEK | kr | Kroon 32 | EGP | ج.م | Egyptian pound 33 | EUR | € | Euro 34 | GBP | £ | Pound sterling 35 | GTQ | Q | Guatemalan Quetzal 36 | HKD | $ | Hong Kong dollar 37 | HRK | kn | Croatian kuna 38 | HUF | Ft | Forint 39 | IDR | Rp | Indonesian rupiah 40 | ILS | ₪ | New Israeli shekel 41 | INR | ₹ | Indian rupee 42 | IRR | IRR | Iranian rial 43 | ISK | kr | Iceland krona 44 | JMD | J$ | Jamaican dollar 45 | JOD | دينار | Jordanian dinar 46 | JPY | ¥ | Japanese yen 47 | KES | KSh | Kenya Shilling 48 | KRW | ₩ | South Korean won 49 | KWD | د.ك | Kuwaiti dinar 50 | KYD | CI$ | Cayman Islands dollar 51 | KZT | KZT | Kazakhstani tenge 52 | LKR | ₨ | Sri Lankan Rupees 53 | LTL | Lt | Lithuanian litas 54 | LVL | Ls | Latvian lats 55 | MAD | MAD | Moroccan Dirham 56 | MDL | MDL | Moldovan leu 57 | MUR | ₨ | Mauritian Rupee 58 | MXN | $ | Mexican peso 59 | MYR | RM | Malaysian Ringgit 60 | NAD | $ | Namibian dollar 61 | NGN | ₦ | Nigerian Naira 62 | NIO | C$ | Nicaraguan córdoba 63 | NOK | kr | Norwegian krone 64 | NZD | $ | New Zealand dollar 65 | OMR | ﷼ | Omani Rial 66 | PEN | S/. | Peruvian nuevo sol 67 | PHP | ₱ | Philippine peso 68 | PKR | ₨ | Pakistani Rupee 69 | PLN | zł | Polish Zloty 70 | PYG | ₲ | Paraguayan Guaraní 71 | QAR | ﷼ | Qatari Riyal 72 | RMB | RMB | Chinese yuan 73 | RON | lei | Romanian new leu 74 | RSD | RSD | Serbian dinar 75 | RUB | руб | Russian ruble 76 | SAR | ريال | Saudi Riyal 77 | SEK | kr | Swedish krona 78 | SGD | SGD | Singapore dollar 79 | SKK | Sk | Slovak koruna 80 | TBH | ฿ | Thai Baht 81 | TND | د.ت | Tunisian Dinar 82 | TRY | TRY | New Turkish lira 83 | TTD | TT$ | Trinidad and Tobago dollar 84 | TWD | NT$ | New Taiwan dollar 85 | U$S | U$S | USD in Uruguay 86 | UAH | ₴ | Ukrainian grivna 87 | US$ | US$ | USD in Australia 88 | USD | $ | United States dollar 89 | UYU | $U | Uruguayan peso 90 | VEF | Bs | Venezuelan bolívar 91 | VND | ₫ | Vietnamese dong 92 | ZAR | ZAR | South African rand 93 | 94 | -------------------------------------------------------------------------------- /sections/workflows.md: -------------------------------------------------------------------------------- 1 | # Workflows 2 | 3 | A project workflow is a set of possible statuses for the project's tasks. 4 | 5 | * [Getting workflows](#list) 6 | * [Getting a workflow](#get) 7 | * [Creating a workflow](#create) 8 | * [Updating a workflow](#update) 9 | * [Deleting a workflow](#delete) 10 | * [The workflow object](#object) 11 | * [Dependent objects](#dependencies) 12 | 13 | 14 | ## Getting workflows 15 | 16 | You can list workflows by making a GET request to: 17 | 18 | * `/api/workflows` for a list of all workflows in your company 19 | * `/api/workflows?include=workflowstatuses` for a list of all workflows with the corresponding workflow statuses 20 | 21 | Example of response: 22 | 23 | ```json 24 | { 25 | "workflows": 26 | [ 27 | { 28 | "id": 1, 29 | "name": "Dev Workflow", 30 | "is_default": true, 31 | "created_on": "2018-08-30T08:49:55Z", 32 | "updated_on": "2018-08-30T08:52:37Z" 33 | }, 34 | { 35 | "id": 2, 36 | "name": "Basic", 37 | "is_default": false, 38 | "created_on": "2018-11-12T13:14:06Z", 39 | "updated_on": "2018-11-12T13:14:06Z" 40 | } 41 | ] 42 | } 43 | ``` 44 | 45 | You can also [include related content](includes.md) when listing workflows. 46 | 47 | 48 | ## Getting a workflow 49 | 50 | To get the workflow's info make a GET request to: 51 | 52 | * `/api/workflows/[WORKFLOW_ID]` 53 | 54 | Example of response: 55 | 56 | ```json 57 | { 58 | "workflows": [ 59 | { 60 | "id": 2, 61 | "name": "Basic", 62 | "is_default": false, 63 | "created_on": "2018-11-12T13:14:06Z", 64 | "updated_on": "2018-11-12T13:14:06Z" 65 | } 66 | ] 67 | } 68 | ``` 69 | 70 | You can also [include related content](includes.md) when getting a workflow. 71 | 72 | 73 | 74 | ## Creating a workflow 75 | 76 | To create a workflow, make a POST request to: 77 | 78 | * `/api/workflows` 79 | 80 | with the request body containing the new workflow info, as in the example below: 81 | 82 | ```json 83 | { 84 | "name": "Design" 85 | } 86 | ``` 87 | 88 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new workflow. The response body will contain the new workflow info as in the **Getting a workflow** section. 89 | 90 | Each new workflow will be created with two default statuses: `Backlog` and `Complete`. To add more statuses see [workflow statuses](workflow_statuses.md#create). 91 | 92 | **Note**: Adding new workflows is not available for free accounts. 93 | 94 | ### Required fields 95 | 96 | When creating a workflow: `name`. 97 | 98 | 99 | ## Updating a workflow 100 | 101 | To update an existing workflow, make a POST or PUT request to: 102 | 103 | * `/api/workflows/[WORKFLOW_ID]` 104 | 105 | with the request body containing the updated info. You can send only the changed fields. 106 | 107 | Example of request body if you want to change the workflow name: 108 | 109 | ```json 110 | { 111 | "name": "Development" 112 | } 113 | ``` 114 | 115 | 116 | ## Deleting a workflow 117 | 118 | To delete a workflow, make a DELETE request to: 119 | 120 | * `/api/workflows/[WORKFLOW_ID]` 121 | 122 | If successful, the response will have a `200 OK` status code. 123 | 124 | ### Warning 125 | 126 | **You cannot delete a workflow that has involved projects!** 127 | 128 | 129 | ## The workflow object 130 | 131 | A workflow object has the following attributes: 132 | 133 | Attribute|Type|Description 134 | ---------|----|----------- 135 | id | integer | _(read-only)_ Unique workflow identifier 136 | name | text | Workflow name 137 | is_default | boolean | Is this workflow the default one for new projects 138 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when 139 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when 140 | 141 | 142 | ## Dependent objects 143 | 144 | The following object types can be used in [includes](includes.md): 145 | 146 | 147 | Object type|Include key|Relationship 148 | -----------|-----------|---- 149 | [Workflow Statuses](workflow_statuses.md) | workflowstatuses | child 150 | -------------------------------------------------------------------------------- /sections/users_tasks.md: -------------------------------------------------------------------------------- 1 | # Task Assignments 2 | 3 | * [Getting task assignments](#list) 4 | * [Getting a task assignment](#get) 5 | * [Creating a task assignment](#create) 6 | * [Updating a task assignment](#update) 7 | * [Deleting a task assignment](#delete) 8 | * [The task assignment object](#object) 9 | * [Dependent objects](#dependencies) 10 | 11 | 12 | 13 | ## Getting task assignments 14 | 15 | You can list task assignments by making a GET request to: 16 | 17 | * `/api/userstasks?where=user_id=[USER_ID]` for a list of task assignments for a user 18 | * `/api/userstasks?where=task_id=[TASK_ID]` for a list of user assignments for a task 19 | 20 | You have to specify at least one `where` condition: `user_id` or `task_id` 21 | 22 | Example of response: 23 | 24 | ```json 25 | { 26 | "userstasks": [ 27 | { 28 | "id": 15200864, 29 | "user_id": 241185, 30 | "task_id": 1536, 31 | "created_on": "2017-02-16T14:43:11Z", 32 | "updated_on": "2017-02-16T14:43:11Z" 33 | }, 34 | { 35 | "id": 15200864, 36 | "user_id": 241185, 37 | "task_id": 1533, 38 | "created_on": "2017-02-16T15:50:14Z", 39 | "updated_on": "2017-02-16T15:50:14Z" 40 | } 41 | ] 42 | } 43 | ``` 44 | 45 | You can also [include related content](includes.md) when listing task assignments. 46 | 47 | 48 | ## Getting a task assignment 49 | 50 | To get the task assignment info, make a GET request to: 51 | 52 | * `/api/userstasks/[ASSIGNMENT_ID]` 53 | 54 | Example response: 55 | 56 | ```json 57 | { 58 | "userstasks": [ 59 | { 60 | "id": 15200864, 61 | "user_id": 241185, 62 | "task_id": 1536, 63 | "created_on": "2017-02-16T14:43:11Z", 64 | "updated_on": "2017-02-16T14:43:11Z" 65 | } 66 | ] 67 | } 68 | ``` 69 | 70 | You can also [include related content](includes.md) when getting a task assignment. 71 | 72 | 73 | ## Creating a task assignment 74 | 75 | To create a task assignment, make a POST request to: 76 | 77 | * `/api/userstasks` 78 | 79 | with the request body containing the new task assignment info, as in the examples below: 80 | 81 | ```json 82 | { 83 | "user_id": 241184, 84 | "task_id": 48591 85 | } 86 | ``` 87 | 88 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new task assignment. The response body will contain the new task assignment info as in the **Getting a task assignment** section. 89 | 90 | ### Required fields 91 | 92 | When creating a task assignment: `user_id` and `task_id`. 93 | 94 | ### Note 95 | 96 | You can also assign users to a task directly by updating the task's `users` attribute. 97 | 98 | 99 | ## Updating a task assignment 100 | 101 | To update an existing task assignment, make a POST or PUT request to: 102 | 103 | * `/api/userstasks/[ASSIGNMENT_ID]` 104 | 105 | with the request body containing the updated info. You can send only the changed fields. 106 | 107 | Example of request body if you want to change the task assignment task_id: 108 | 109 | ```json 110 | { 111 | "task_id": 4059 112 | } 113 | ``` 114 | 115 | The response will return `200 OK` and will contain the updated task assignment info as in the **Getting a task assignment** section. 116 | 117 | 118 | ## Deleting a task assignment 119 | 120 | To delete a task assignment, make a DELETE request to: 121 | 122 | * `/api/userstasks/[ASSIGNMENT_ID]` 123 | 124 | If successful, the response will have a `200 OK` status code. 125 | 126 | 127 | ## The task assignment object 128 | 129 | A task assignment object has the following attributes: 130 | 131 | Attribute|Type|Description 132 | ---------|----|----------- 133 | id | integer | _(read-only)_ Unique task assignment identifier 134 | user_id | integer | Id of the user 135 | task_id | integer | Id of the task 136 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the task assignment was created 137 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the task assignment was last updated 138 | 139 | 140 | ## Dependent objects 141 | 142 | The following object types can be used in [includes](includes.md): 143 | 144 | Object type|Include key|Relationship 145 | -----------|-----------|---- 146 | [User](users.md) | user | parent 147 | [Task](tasks.md) | task | parent 148 | -------------------------------------------------------------------------------- /sections/bookings.md: -------------------------------------------------------------------------------- 1 | # Bookings (Resource Scheduling) 2 | 3 | * [Getting bookings](#list) 4 | * [Getting a booking](#get) 5 | * [Creating a booking](#create) 6 | * [Updating a booking](#update) 7 | * [Deleting a booking](#delete) 8 | * [The booking object](#object) 9 | * [Dependent objects](#dependencies) 10 | 11 | 12 | 13 | ## Getting bookings 14 | 15 | You can list bookings by making a GET request to: 16 | 17 | * `/api/bookings?where=project_id=[PROJECT_ID]` for a list of bookings from a project 18 | * `/api/bookings?where=task_id=[TASK_ID]` for a list of bookings for a task 19 | * `/api/bookings?where=user_id=[USER_ID]` for a list of bookings for a user 20 | * `/api/bookings?where=user_id=[USER_TASK_ID]` for a list of bookings for task assignment 21 | * `/api/bookings?where=date_interval in ("2016-12-01","2017-01-01")` for a list of bookings between Dec 1st, 2016 and Jan 1st, 2017. 22 | 23 | You have to specify at least one `where` condition: `user_task_id`, or `task_id`, or `project_id`, or `user_id`, or `start_date` and `end_date` 24 | 25 | Example of response: 26 | 27 | ```json 28 | { 29 | "bookings": [ 30 | { 31 | "id": 15200864, 32 | "user_task_id": 241184, 33 | "start_date": "2016-12-11", 34 | "end_date": "2016-12-12", 35 | "hours_per_day": 10, 36 | "description": "Plan the meeting", 37 | "created_on": "2014-12-12T14:42:49Z", 38 | "updated_on": "2014-12-12T14:42:49Z" 39 | }, 40 | { 41 | "id": 15200864, 42 | "user_task_id": 241185, 43 | "start_date": "2016-12-15", 44 | "end_date": "2016-12-25", 45 | "hours_per_day": 8, 46 | "description": null, 47 | "created_on": "2014-12-12T14:42:49Z", 48 | "updated_on": "2014-12-12T14:42:49Z" 49 | } 50 | ] 51 | } 52 | ``` 53 | 54 | You can also [include related content](includes.md) when listing bookings. 55 | 56 | 57 | ## Getting a booking 58 | 59 | To get the booking info, make a GET request to: 60 | 61 | * `/api/bookings/[BOOKING_ID]` 62 | 63 | Example response: 64 | 65 | ```json 66 | { 67 | "bookings": [ 68 | { 69 | "id": 15200864, 70 | "user_task_id": 241185, 71 | "start_date": "2016-12-15", 72 | "end_date": "2016-12-25", 73 | "hours_per_day": 4, 74 | "description": null, 75 | "created_on": "2014-12-12T14:42:49Z", 76 | "updated_on": "2014-12-12T14:42:49Z" 77 | } 78 | ] 79 | } 80 | ``` 81 | 82 | You can also [include related content](includes.md) when getting a booking. 83 | 84 | 85 | ## Creating a booking 86 | 87 | To create a booking, make a POST request to: 88 | 89 | * `/api/bookings` 90 | 91 | with the request body containing the new booking info, as in the examples below: 92 | 93 | ```json 94 | { 95 | "user_task_id": 241184, 96 | "start_date": "2016-12-10", 97 | "end_date": "2016-12-30", 98 | "hours_per_day": 5, 99 | "description": "Plan the meeting" 100 | } 101 | ``` 102 | 103 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new booking. The response body will contain the new booking info as in the **Getting a booking** section. 104 | 105 | * Important 106 | UserTask required... 107 | 108 | ### Required fields 109 | 110 | When creating a booking: `user_task_id`, `start_date`, `end_date` and `hours_per_day`. 111 | 112 | 113 | ## Updating a booking 114 | 115 | To update an existing booking, make a POST or PUT request to: 116 | 117 | * `/api/bookings/[BOOKING_ID]` 118 | 119 | with the request body containing the updated info. You can send only the changed fields. 120 | 121 | Example of request body if you want to change the booking start date: 122 | 123 | ```json 124 | { 125 | "start_date": "2016-12-10" 126 | } 127 | ``` 128 | 129 | The response will return `200 OK` and will contain the updated booking info as in the **Getting a booking** section. 130 | 131 | 132 | ## Deleting a booking 133 | 134 | To delete a booking, make a DELETE request to: 135 | 136 | * `/api/bookings/[BOOKING_ID]` 137 | 138 | If successful, the response will have a `200 OK` status code. 139 | 140 | 141 | ## The booking object 142 | 143 | A booking object has the following attributes: 144 | 145 | Attribute|Type|Description 146 | ---------|----|----------- 147 | id | integer | _(read-only)_ Unique booking identifier 148 | user_task_id | integer | Id of the user-task assignment 149 | start_date | date | Date when the booking starts 150 | end_date | date | Date when the booking ends 151 | hours_per_day | integer | Allocated hours per day 152 | description | text | Booking description 153 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the booking was created 154 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the booking was last updated 155 | 156 | 157 | ## Dependent objects 158 | 159 | The following object types can be used in [includes](includes.md): 160 | 161 | Object type|Include key|Relationship 162 | -----------|-----------|---- 163 | [UserTask](users_tasks.md) | usertask | parent 164 | -------------------------------------------------------------------------------- /sections/workflow_statuses.md: -------------------------------------------------------------------------------- 1 | # Workflow statuses 2 | 3 | * [Getting workflow statuses](#list) 4 | * [Getting a workflow status](#get) 5 | * [Creating a workflow status](#create) 6 | * [Updating a workflow status](#update) 7 | * [Deleting a workflow status](#delete) 8 | * [The workflow status object](#object) 9 | * [Dependent objects](#dependencies) 10 | 11 | 12 | ## Getting workflow statuses 13 | 14 | You can list workflow statuses by making a GET request to: 15 | 16 | * `/api/workflowstatuses` for a list of all workflow statuses from all workflows 17 | * `/api/workflowstatuses?where=workflow_id=[WORKFLOW_ID]` for a list of workflow statuses for a specific workflow 18 | 19 | Example of response: 20 | 21 | ```json 22 | { 23 | "workflowstatuses": [ 24 | { 25 | "id": 1, 26 | "workflow_id": 1, 27 | "name": "Backlog", 28 | "color": null, 29 | "action": "backlog", 30 | "created_on": "2018-12-04T13:22:36Z", 31 | "updated_on": "2018-12-04T13:22:36Z" 32 | }, 33 | { 34 | "id": 2, 35 | "workflow_id": 1, 36 | "name": "To Do", 37 | "color": "5BDBF6", 38 | "action": "", 39 | "created_on": "2018-12-04T13:22:36Z", 40 | "updated_on": "2018-12-04T13:22:36Z" 41 | }, 42 | { 43 | "id": 3, 44 | "workflow_id": 1, 45 | "name": "In progress", 46 | "color": "FFB855", 47 | "action": "", 48 | "created_on": "2018-12-04T13:22:36Z", 49 | "updated_on": "2018-12-04T13:22:36Z" 50 | }, 51 | { 52 | "id": 4, 53 | "workflow_id": 1, 54 | "name": "Complete", 55 | "color": "3E993C", 56 | "action": "complete", 57 | "created_on": "2018-12-04T13:22:36Z", 58 | "updated_on": "2018-12-04T13:22:36Z" 59 | } 60 | ] 61 | } 62 | ``` 63 | 64 | You can also [include related content](includes.md) when listing workflow statuses. 65 | 66 | 67 | ## Getting a workflow status 68 | 69 | To get the workflow status info, make a GET request to: 70 | 71 | * `/api/workflowstatuses/[WORKFLOW_STATUS_ID]` 72 | 73 | Example response: 74 | 75 | ```json 76 | { 77 | "workflowstatuses":[ 78 | { 79 | "id":1, 80 | "workflow_id":1, 81 | "name":"Backlog", 82 | "color":null, 83 | "action":"backlog", 84 | "created_on":"2018-11-21T08:29:15Z", 85 | "updated_on":"2018-11-21T08:29:15Z" 86 | } 87 | ] 88 | } 89 | ``` 90 | 91 | You can also [include related content](includes.md) when getting a workflow status. 92 | 93 | 94 | ## Creating a workflow status 95 | 96 | To create a workflow status, make a POST request to: 97 | 98 | * `/api/workflowstatuses` 99 | 100 | ```json 101 | { 102 | "name": "New Workflow Status", 103 | "color": "FF0000", 104 | "workflow_id": 1 105 | } 106 | ``` 107 | 108 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new workflow status. The response body will contain the new workflow status info as in the **Getting a workflow status** section. 109 | 110 | ### Required fields 111 | 112 | When creating a workflow status: `name`, `color`, `workflow_id`. 113 | 114 | 115 | ## Updating a workflow status 116 | 117 | To update an existing workflow status, make a POST or PUT request to: 118 | 119 | * `/api/workflowstatuses/[WORKFLOW_STATUS_ID]` 120 | 121 | with the request body containing the updated info. You can send only the changed fields. 122 | 123 | Example of request body if you want to change the name of the workflow status: 124 | 125 | ```json 126 | { 127 | "name": "This is the new name" 128 | } 129 | ``` 130 | 131 | The response will return `200 OK` and will contain the updated workflow status info as in the **Getting a workflow status** section. 132 | 133 | 134 | ## Deleting a workflow status 135 | 136 | To delete a workflow status, make a DELETE request to: 137 | 138 | * `/api/workflowstatuses/[WORKFLOW_STATUS_ID]` 139 | 140 | If successful, the response will have a `200 OK` status code. 141 | 142 | **You cannot delete a workflow status that has involved tasks!** 143 | 144 | 145 | ## The workflow status object 146 | 147 | A workflow status object has the following attributes: 148 | 149 | Attribute|Type|Description 150 | ---------|----|----- 151 | id | integer | _(read-only)_ Unique workflow status identifier 152 | name | text | Workflow status name 153 | workflow_id | integer | The workflow id for whom the workflow status was created 154 | color | text | An RGB value representing a color for the workflow status when used in board. 155 | seq | integer | _(read-only)_ Position (order) of the workflow status in the workflow 156 | action | text | _(read-only)_ An identifier for the Backlog and Complete statuses 157 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the workflow status was created 158 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the workflow status was last updated 159 | 160 | 161 | 162 | ## Dependent objects 163 | 164 | The following object types can be used in [includes](includes.md): 165 | 166 | Object type|Include key|Relationship 167 | -----------|-----------|---- 168 | [Workflow](workflows.md) | workflow | parent 169 | -------------------------------------------------------------------------------- /sections/invoice_payments.md: -------------------------------------------------------------------------------- 1 | # Invoice payments 2 | 3 | * [Listing invoice payments](#list) 4 | * [Getting an invoice payment](#get) 5 | * [Creating an invoice payment](#create) 6 | * [Updating an invoice payment](#update) 7 | * [Deleting an invoice payment](#delete) 8 | * [The invoice payment object](#object) 9 | * [Dependent objects](#dependencies) 10 | 11 | 12 | ## Listing invoice payments 13 | 14 | You can list payments by making a GET request to: 15 | 16 | * `/api/invoicepayments` for a list of all payments 17 | * `/api/invoicepayments?where=invoice_id=[INVOICE_ID]` for a list of payments for an invoice 18 | 19 | Example of response: 20 | 21 | ```json 22 | { 23 | "invoicepayments": [ 24 | { 25 | "id": 64547, 26 | "invoice_id": 9876, 27 | "amount": 15.25, 28 | "date": "2016-09-28", 29 | "notes": "", 30 | "created_on": "2016-09-28T19:20:03Z", 31 | "updated_on": "2016-09-28T19:20:03Z" 32 | }, 33 | { 34 | "id": 64548, 35 | "invoice_id": 9876, 36 | "amount": 10, 37 | "date": "2016-09-29", 38 | "notes": "", 39 | "created_on": "2016-09-29T09:31:41Z", 40 | "updated_on": "2016-09-29T09:31:41Z" 41 | } 42 | ] 43 | } 44 | ``` 45 | 46 | 47 | ## Getting an invoice payment 48 | 49 | To get the invoice payment info, make a GET request to: 50 | 51 | * `/api/invoicepayments/[PAYMENT_ID]` 52 | 53 | Example response: 54 | 55 | ```json 56 | { 57 | "invoicepayments": [ 58 | { 59 | "id": 64547, 60 | "invoice_id": 9876, 61 | "amount": 15.25, 62 | "date": "2016-09-28", 63 | "notes": "", 64 | "created_on": "2016-09-28T19:20:03Z", 65 | "updated_on": "2016-09-28T19:20:03Z" 66 | } 67 | ] 68 | } 69 | ``` 70 | 71 | 72 | ## Adding an invoice payment 73 | 74 | To add an invoice payment, make a POST request to: 75 | 76 | * `/api/invoicepayments` 77 | 78 | with the request body containing the new payment info. 79 | 80 | Sample request body to create a payment of 10 USD (currency is set in the invoice): 81 | 82 | ```json 83 | { 84 | "invoice_id": 64549, 85 | "amount": 10 86 | } 87 | ``` 88 | 89 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new invoice payment. The response body will contain the new invoice payment info as in the **Getting an invoice payment** section. 90 | 91 | Add payment request will fail if the payment amount is greater than the remaining amount to be paid for the invoice. 92 | 93 | ### Required fields 94 | 95 | When creating an invoice payment: `invoice_id` and `amount`. 96 | For a complete description of all invoice payment fields, see [invoice payment object](#object). 97 | 98 | ### Changing invoice status to paid 99 | 100 | After adding a payment, if the sum of all the payments for the invoice equals the invoice total, the invoice will change its status to `paid` automatically. 101 | 102 | 103 | ## Updating an invoice payment 104 | 105 | To update an existing invoice payment, make a POST or PUT request to: 106 | 107 | * `/api/invoicepayments/[PAYMENT_ID]` 108 | 109 | with the request body containing the updated info. You can send only the changed fields. 110 | 111 | Example of request body if you want to add a not for the payment: 112 | 113 | ```json 114 | { 115 | "note": "A note for the payment" 116 | } 117 | ``` 118 | 119 | ### Changing invoice status after updating a payment 120 | 121 | After updating an invoice payment amount, the invoice status might change depending on the new sum of payments for the invoice. 122 | 123 | If the sum of all invoice payments equals the invoice total, the invoice status will change to `paid`. 124 | 125 | If the invoice had a status of `paid` and the sum of all invoice payments becomes less than the invoice total, the invoice status will change from `paid` to `viewed`. 126 | 127 | 128 | ## Deleting an invoice payment 129 | 130 | To delete an invoice payment, make a DELETE request to: 131 | 132 | * `/api/invoicepayments/[PAYMENT_ID]` 133 | 134 | If successful, the response will have a `200 OK` status code. 135 | 136 | ### Changing invoice status after deleting a payment 137 | 138 | If the invoice had a status of `paid` and the sum of all invoice payments becomes less than the invoice total, the invoice status will change from `paid` to `viewed`. 139 | 140 | 141 | ## The invoice payment object 142 | 143 | An invoice payment object has the following attributes: 144 | 145 | Attribute|Type|Description 146 | ---------|----|----------- 147 | id | integer | _(read-only)_ Unique payment identifier 148 | invoice_id | integer | _(read-only)_ Id of the invoice this payment belongs to 149 | amount | decimal | Payment amount. The currency is specified in the related invoice. 150 | date | date | A user set date of the payment 151 | notes | text | Payment notes or description 152 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the invoice payment was created 153 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the invoice payment was last updated 154 | 155 | Note that the invoice payment has no `currency` attribute. It has the same currency as the invoice it belongs to. 156 | 157 | 158 | ## Dependent objects 159 | 160 | The following object types can be used in [includes](includes.md): 161 | 162 | Object type|Include key|Relationship 163 | -----------|-----------|---- 164 | [Invoice](invoices.md) | invoice | parent 165 | -------------------------------------------------------------------------------- /sections/tasklists.md: -------------------------------------------------------------------------------- 1 | # Task lists 2 | 3 | * [Getting task lists](#list) 4 | * [Getting a task list](#get) 5 | * [Creating a task list](#create) 6 | * [Updating a task list](#update) 7 | * [Changing the order of task lists](#update-tasklists-order) 8 | * [Changing the order of tasks](#update-tasks-order) 9 | * [Deleting a task list](#delete) 10 | * [The task list object](#object) 11 | * [Dependent objects](#dependencies) 12 | 13 | 14 | ## Getting task lists 15 | 16 | You can list task lists by making a GET request to: 17 | 18 | * `/api/tasklists` for a list of all task lists from all projects 19 | * `/api/tasklists?where=project_id=[PROJECT_ID]` for a list of task lists from a project 20 | * `/api/tasklists?where=milestone_id=[MILESTONE_ID]` for a list of task lists linked with a milestone 21 | 22 | Example of response: 23 | 24 | ```json 25 | { 26 | "tasklists": [ 27 | { 28 | "id": 499, 29 | "name": "Design", 30 | "project_id": 1, 31 | "seq": 1, 32 | "milestone_id": 3, 33 | "created_on": "2013-06-26T12:07:44Z", 34 | "updated_on": "2014-07-23T14:22:05Z" 35 | }, 36 | { 37 | "id": 518, 38 | "name": "Coding", 39 | "project_id": 1, 40 | "seq": 2, 41 | "milestone_id": null, 42 | "created_on": "2013-06-26T12:07:44Z", 43 | "updated_on": "2014-07-23T14:22:05Z" 44 | } 45 | ] 46 | } 47 | ``` 48 | 49 | You can also [include related content](includes.md) when listing task lists. 50 | 51 | 52 | ## Getting a task list 53 | 54 | To get the task list info, make a GET request to: 55 | 56 | * `/api/tasklists/[TASKLIST_ID]` 57 | 58 | Example response: 59 | 60 | ```json 61 | { 62 | "tasklists": [ 63 | { 64 | "id": 499, 65 | "name": "Design", 66 | "project_id": 1, 67 | "seq": 1, 68 | "milestone_id": 3, 69 | "created_on": "2013-06-26T12:07:44Z", 70 | "updated_on": "2014-07-23T14:22:05Z" 71 | } 72 | ] 73 | } 74 | ``` 75 | 76 | You can also [include related content](includes.md) when getting a task list. 77 | 78 | 79 | ## Creating a task list 80 | 81 | To create a task list, make a POST request to: 82 | 83 | * `/api/tasklists` 84 | 85 | with the request body containing the new task list info, as in the example below: 86 | 87 | ```json 88 | { 89 | "name": "Website Design", 90 | "project_id": 100 91 | } 92 | ``` 93 | 94 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new task list. The response body will contain the new task list info as in the **Getting a task list** section. 95 | 96 | ### Required fields 97 | 98 | When creating a task list: `name`, `project_id`. 99 | 100 | 101 | ## Updating a task list 102 | 103 | To update an existing task list, make a POST or PUT request to: 104 | 105 | * `/api/tasklists/[TASKLIST_ID]` 106 | 107 | with the request body containing the updated info. You can send only the changed fields. 108 | 109 | Example of request body if you want to change the tasklist name: 110 | 111 | ```json 112 | { 113 | "name": "Urgent tasks" 114 | } 115 | ``` 116 | 117 | The response will return `200 OK` and will contain the updated task list info as in the **Getting a task list** section. 118 | 119 | 120 | ## Changing the order of task lists 121 | 122 | To reorder the task lists in a project you need to make an **[update project](projects.md#update-tasklists-order)** request with a body similar to: 123 | 124 | ```json 125 | { 126 | "tasklists_order": [ 493, 50, 128, 2, 4 ] 127 | } 128 | ``` 129 | 130 | where `tasklists_order` is a list of task list ids in the new order. 131 | 132 | 133 | ## Changing the order of tasks 134 | 135 | To reorder the tasks in a task list you need to make an **update task list** request with a body similar to: 136 | 137 | ```json 138 | { 139 | "tasks_order": [ 39, 2, 10, 9, 11, 12, 209 ] 140 | } 141 | ``` 142 | 143 | where `tasks_order` is a list of task ids in the new order. 144 | 145 | **Note**: The order of tasks is defined per task list, not per project. 146 | 147 | 148 | ## Deleting a task list 149 | 150 | To delete a task list, make a DELETE request to: 151 | 152 | * `/api/tasklists/[TASKLIST_ID]` 153 | 154 | If successful, the response will have a `200 OK` status code. 155 | 156 | ### Warning 157 | 158 | **Deleting a task list will also delete all other data from that tasklist: tasks and time entries! 159 | 160 | 161 | ## The task list object 162 | 163 | A task list object has the following attributes: 164 | 165 | Attribute|Type|Description 166 | ---------|----|----------- 167 | id | integer | _(read-only)_ Unique task list identifier 168 | name | text | Task list name 169 | seq | integer | Position (order) of the task list in the project 170 | project_id | integer | _(read-only)_ Project id 171 | milestone_id | integer | Id of the milestone it is linked with. If a task list is linked with a milestone, all tasks from the task list should be completed by the milestone due date. 172 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the list was created 173 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the list was last updated 174 | 175 | 176 | ## Dependent objects 177 | 178 | The following object types can be used in [includes](includes.md): 179 | 180 | Object type|Include key|Relationship 181 | -----------|-----------|---- 182 | [Project](projects.md) | project | parent 183 | [Milestone](milestones.md) | milestone | parent 184 | [Task](tasks.md) | tasks | child 185 | 186 | -------------------------------------------------------------------------------- /sections/milestones.md: -------------------------------------------------------------------------------- 1 | # Milestones 2 | 3 | * [Getting milestones](#list) 4 | * [Getting a milestone](#get) 5 | * [Creating a milestone](#create) 6 | * [Updating a milestone](#update) 7 | * [Completing a milestone](#complete) 8 | * [Deleting a milestone](#delete) 9 | * [The milestone object](#object) 10 | * [Dependent objects](#dependencies) 11 | 12 | 13 | ## Getting milestones 14 | 15 | You can list milestones by making a GET request to: 16 | 17 | * `/api/milestones` for a list of all the milestones 18 | * `/api/milestones?where=due_date=2014-09-01` for a list of milestones that are due on Sep 1st, 2014 19 | * `/api/milestones?where=due_date>2014-12-15 and complete=false` for a list of milestones that are due after Dec 15, 2014 and are not complete 20 | * `/api/milestones?where=user_id=[USER_ID]` for a list of milestones assigned to a user 21 | 22 | Example of response: 23 | 24 | ```json 25 | { 26 | "milestones": [ 27 | { 28 | "id": 60805, 29 | "name": "Integration", 30 | "project_id": 397707, 31 | "user_id": 23129, 32 | "due_date": "2014-11-23", 33 | "send_reminder": 0, 34 | "reminder_sent": true, 35 | "complete": false, 36 | "created_on": "2014-10-03T12:49:06Z", 37 | "updated_on": "2014-11-22T14:34:59Z", 38 | "linked_tasklists": [] 39 | }, 40 | { 41 | "id": 60806, 42 | "name": "Requirements", 43 | "project_id": 397717, 44 | "user_id": 23129, 45 | "due_date": "2014-11-11", 46 | "send_reminder": 48, 47 | "reminder_sent": false, 48 | "complete": false, 49 | "created_on": "2014-10-03T12:49:06Z", 50 | "updated_on": "2014-10-24T14:34:59Z", 51 | "linked_tasklists": [ 52 | 629824 53 | ] 54 | } 55 | ] 56 | } 57 | ``` 58 | 59 | You can also [include related content](includes.md) when listing milestones. 60 | 61 | 62 | ## Getting a milestone 63 | 64 | To get the milestone info, make a GET request to: 65 | 66 | * `/api/milestones/[MILESTONE_ID]` 67 | 68 | Example response: 69 | 70 | ```json 71 | { 72 | "milestones": [ 73 | { 74 | "id": 60805, 75 | "name": "Integration", 76 | "project_id": 397707, 77 | "user_id": 23129, 78 | "due_date": "2014-11-23", 79 | "send_reminder": 0, 80 | "reminder_sent": true, 81 | "complete": false, 82 | "created_on": "2014-10-03T12:49:06Z", 83 | "updated_on": "2014-11-22T14:34:59Z", 84 | "linked_tasklists": [] 85 | } 86 | ] 87 | } 88 | ``` 89 | 90 | You can also [include related content](includes.md) when getting a milestone. 91 | 92 | 93 | ## Creating a milestone 94 | 95 | To create a milestone, make a POST request to: 96 | 97 | * `/api/milestones` 98 | 99 | with the request body containing the new milestone info, as in the example below: 100 | 101 | ```json 102 | { 103 | "name": "Documentation", 104 | "project_id": 1234, 105 | "due_date": "2015-01-15", 106 | "user_id": 390 107 | } 108 | ``` 109 | 110 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new milestone. The response body will contain the new milestone info as in the **Getting a milestone** section. 111 | 112 | ### Required fields 113 | 114 | When creating a milestone: `name`, `project_id`, `due_date`. 115 | 116 | 117 | ## Updating a milestone 118 | 119 | To update an existing milestone, make a POST or PUT request to: 120 | 121 | * `/api/milestones/[MILESTONE_ID]` 122 | 123 | with the request body containing the updated info. You can send only the changed fields. 124 | 125 | Example of request body if you want to change the milestone due date: 126 | 127 | ```json 128 | { 129 | "due_date": "2015-02-15" 130 | } 131 | ``` 132 | 133 | The response will return `200 OK` and will contain the updated milestone info as in the **Getting a milestone** section. 134 | 135 | 136 | ## Completing a milestone 137 | 138 | To mark a milestone as complete, make an update request with the following body: 139 | 140 | ```json 141 | { 142 | "complete": true 143 | } 144 | ``` 145 | 146 | 147 | ## Deleting a milestone 148 | 149 | To delete a milestone, make a DELETE request to: 150 | 151 | * `/api/milestones/[MILESTONE_ID]` 152 | 153 | If successful, the response will have a `200 OK` status code. 154 | 155 | 156 | ## The milestone object 157 | 158 | A milestone object has the following attributes: 159 | 160 | Attribute|Type|Description 161 | ---------|----|----------- 162 | id | integer | _(read-only)_ Unique milestone identifier 163 | name | text | Milestone name 164 | project_id | integer | Project id 165 | user_id | integer | Id of the user responsible for the milestone. This user will receive late milestone reminders. 166 | due_date | [date](datetime.md) | Due date for the milestone 167 | send_reminder | integer | How many hours before the due date the reminders will be sent. If `0`, no reminders will be sent. 168 | reminder_sent | boolean | _(read-only)_ If `true` the reminder was sent. 169 | complete | boolean | If `true` the milestone is marked as completed. 170 | linked_tasklists | list | List of task list ids that are linked with this milestone. 171 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the milestone was created 172 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the milestone was last updated 173 | 174 | 175 | ## Dependent objects 176 | 177 | The following object types can be used in [includes](includes.md): 178 | 179 | Object type|Include key|Relationship 180 | -----------|-----------|---- 181 | [Project](projects.md) | project | parent 182 | [User](users.md) | user | parent 183 | [Task List](tasklists.md) | tasklists | child 184 | -------------------------------------------------------------------------------- /sections/discussions.md: -------------------------------------------------------------------------------- 1 | # Discussions 2 | 3 | * [Getting discussions](#list) 4 | * [Getting a discussion](#get) 5 | * [Creating a discussion](#create) 6 | * [Updating a discussion](#update) 7 | * [Adding a file to a discussion](#add-file) 8 | * [Deleting a discussion](#delete) 9 | * [The discussion object](#object) 10 | * [Dependent objects](#dependencies) 11 | 12 | 13 | ## Getting discussions 14 | 15 | You can list discussions by making a GET request to: 16 | 17 | * `/api/discussions` for a list of all the discussions 18 | * `/api/discussions?where=project_id=[PROJECT_ID]` for a list of discussions from a project 19 | * `/api/discussions?where=user_id=[USER_ID]` for a list of discussions started by a user 20 | 21 | Example of response: 22 | 23 | ```json 24 | { 25 | "discussions": [ 26 | { 27 | "id": 10983, 28 | "name": "Preliminary research", 29 | "description": "We need to define exactly all the steps required for this preliminary research.", 30 | "project_id": 347452, 31 | "user_id": 1563, 32 | "created_on": "2014-10-01T12:04:07Z", 33 | "updated_on": "2014-10-01T12:11:05Z" 34 | }, 35 | { 36 | "id": 9879, 37 | "name": "EA design", 38 | "description": "Who has access to the old EA Games design?", 39 | "project_id": 347452, 40 | "user_id": 1647, 41 | "created_on": "2014-07-23T14:22:05Z", 42 | "updated_on": "2014-07-23T14:22:05Z" 43 | } 44 | ] 45 | } 46 | ``` 47 | 48 | You can also [include related content](includes.md) when listing discussions. 49 | 50 | 51 | ## Getting a discussion 52 | 53 | To get the discussion info, make a GET request to: 54 | 55 | * `/api/discussions/[DISCUSSION_ID]` 56 | * `/api/discussions/[DISCUSSION_ID]?include=files` for discussion info with a list of attached files 57 | * `/api/discussions/[DISCUSSION_ID]?include=thread.comments` for discussion info with a list of discussion comments 58 | 59 | Example response: 60 | 61 | ```json 62 | { 63 | "discussions": [ 64 | { 65 | "id": 10983, 66 | "name": "Preliminary research", 67 | "description": "We need to define exactly all the steps required for this preliminary research.", 68 | "project_id": 347452, 69 | "user_id": 1563, 70 | "created_on": "2014-10-01T12:04:07Z", 71 | "updated_on": "2014-10-01T12:11:05Z" 72 | } 73 | ] 74 | } 75 | ``` 76 | 77 | You can also [include related content](includes.md) when getting a discussion. 78 | 79 | 80 | ## Creating a discussion 81 | 82 | To create a discussion, make a POST request to: 83 | 84 | * `/api/discussions` 85 | 86 | with the request body containing the new discussion info, as in the example below: 87 | 88 | ```json 89 | { 90 | "name": "Where do we start?" 91 | } 92 | ``` 93 | 94 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new discussion. The response body will contain the new discussion info as in the **Getting a discussion** section. 95 | 96 | ### Required fields 97 | 98 | When creating a discussion: `name`, `project_id`. 99 | 100 | 101 | ## Updating a discussion 102 | 103 | To update an existing discussion, make a POST or PUT request to: 104 | 105 | * `/api/discussions/[DISCUSSION_ID]` 106 | 107 | with the request body containing the updated info. You can send only the changed fields. 108 | 109 | Example of request body if you want to change the discussion description: 110 | 111 | ```json 112 | { 113 | "description": "All required information should be posted in here" 114 | } 115 | ``` 116 | 117 | The response will return `200 OK` and will contain the updated discussion info as in the **Getting a discussion** section. 118 | 119 | 120 | ## Adding a file to a discussion 121 | 122 | To add a file to a discussion, make a POST request to: 123 | 124 | * `/api/discussions/[DISCUSSION_ID]` 125 | 126 | The request content-type should be `multipart-form-data` and the file field name equal to `file`. Here's an example using `curl` command line: 127 | 128 | ```shell 129 | curl -u email:password 130 | -H 'Accept: application/json' 131 | -F "file=@plan.doc" 132 | https://app.paymoapp.com/api/discussions/12345 133 | ``` 134 | 135 | The file can also be added when creating the discussion. In that case, all the discussion fields should be send in `multipart-form-data` format together with the file. 136 | 137 | 138 | ## Deleting a discussion 139 | 140 | To delete a discussion, make a DELETE request to: 141 | 142 | * `/api/discussions/[DISCUSSION_ID]` 143 | 144 | If successful, the response will have a `200 OK` status code. 145 | 146 | ### Warning 147 | 148 | **Deleting a discussion will also delete all comments from that discussion!** 149 | 150 | 151 | ## The discussion object 152 | 153 | A discussion object has the following attributes: 154 | 155 | Attribute|Type|Description 156 | ---------|----|----------- 157 | id | integer | _(read-only)_ Unique discussion identifier 158 | name | text | Discussion name 159 | description | html | Discussion description 160 | project_id | integer | Id of the project the discussion is about 161 | user_id | integer | _(read-only)_ Id of the user that started the discussion 162 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the discussion was created 163 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the discussion was last updated 164 | 165 | 166 | ## Dependent objects 167 | 168 | The following object types can be used in [includes](includes.md): 169 | 170 | Object type|Include key|Relationship 171 | -----------|-----------|---- 172 | [Project](projects.md) | project | parent 173 | [User](users.md) | user | parent 174 | [Comments thread](comments.md) | thread | parent 175 | [File](files.md) | files | child 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Paymo is a [project management software](https://www.paymoapp.com/project-management/) for small and medium businesses that allows you to manage projects from start to finish. 2 | 3 | This is the official Paymo API. 4 | 5 | # Paymo API 6 | 7 | * [Making a request](#make-request) 8 | * [Authentication](#authentication) 9 | * [Request and response content types](#content-types) 10 | * [Response codes and error handling](#response-codes) 11 | * [Rate limiting](#rate-limit) 12 | * [API Endpoints](#api-endpoints) 13 | * [Data formats](#data-formats) 14 | * [Filtering](#filtering) 15 | * [Including related content](#includes) 16 | * [Webhooks](#webhooks) 17 | 18 | 19 | The Paymo is a [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) API that uses JSON/XML for serialization. 20 | 21 | Accepted request types (HTTP verbs) are: GET, POST, PUT, DELETE. 22 | 23 | 24 | ## Making a request 25 | 26 | The API base URL is `https://app.paymoapp.com/api/`. It is **SSL/TLS 1.2 only**. There is no way to use the API over unsecure http:// protocol. 27 | 28 | An example request using `curl` for retrieving the list of clients may look like this: 29 | 30 | ```shell 31 | curl -u email:password 32 | -H 'Accept: application/json' 33 | https://app.paymoapp.com/api/clients 34 | ``` 35 | 36 | Updating a client using `curl` may look like: 37 | 38 | ```shell 39 | curl -u email:password 40 | -H 'Accept: application/json' 41 | -d "name=updated%20name" 42 | https://app.paymoapp.com/api/clients/12345 43 | ``` 44 | 45 | See sample [code examples](sections/sample_code.md) on how to make a request to the API. 46 | 47 | 48 | ## Authentication 49 | 50 | Read the [authentication guide](https://github.com/paymoapp/api/blob/master/sections/authentication.md) for more details. 51 | 52 | 53 | ## Request and response content types 54 | 55 | Paymo API supports JSON as well as XML for data serialization, as well as specific types such as PDF, XLS for reports, invoices, estimates. 56 | 57 | Read more about [content types](sections/content_types.md). 58 | 59 | 60 | ## Response codes and error handling 61 | 62 | The Paymo API will return a 2xx status code for successful requests. The 4xx error means an error on the user side. And the 5xx errors are returned when the Paymo service is having trouble processing your request. 63 | 64 | The response in case of error will contain an error message to help you fix it. 65 | 66 | You may want to consult a reference for [HTTP Status codes](http://en.wikipedia.org/wiki/List_of_HTTP_status_codes). 67 | 68 | 69 | ## Rate limiting 70 | 71 | If you exceed the rate limit, you'll get a [429 Too Many Requests](http://tools.ietf.org/html/draft-nottingham-http-new-status-02#section-4) response and for all following requests until the limit expires. A header `Retry-After` will also be returned and will represent the number of seconds you should wait before making the next request. 72 | 73 | The following response headers will help you with the current API usage: 74 | 75 | - `X-Ratelimit-Decay-Period`: the length in seconds of the current time interval for which the limits are applied 76 | - `X-Ratelimit-Limit`: the total number of API requests allowed per decay period 77 | - `X-Ratelimit-Remaining`: the number of remaining API requests in the current decay period 78 | 79 | 80 | ## API endpoints 81 | 82 | * [Bookings (Resource Scheduling)](sections/bookings.md) 83 | * [Client Contacts](sections/client_contacts.md) 84 | * [Clients](sections/clients.md) 85 | * [Comments](sections/comments.md) 86 | * [Company](sections/company.md) 87 | * [Discussions](sections/discussions.md) 88 | * [Estimate Templates](sections/estimate_templates.md) 89 | * [Estimates](sections/estimates.md) 90 | * [Expenses](sections/expenses.md) 91 | * [Files](sections/files.md) 92 | * [Invoice Templates](sections/invoice_templates.md) 93 | * [Invoice Payments](sections/invoice_payments.md) 94 | * [Invoice Recurring Profiles](sections/invoice_recurring_profiles.md) 95 | * [Invoices](sections/invoices.md) 96 | * [Milestones](sections/milestones.md) 97 | * [Project Templates](sections/project_templates.md) 98 | * [Project Statuses](sections/project_statuses.md) 99 | * [Projects](sections/projects.md) 100 | * [Reports](sections/reports.md) 101 | * [Subtasks](sections/subtasks.md) 102 | * [Sessions](sections/sessions.md) 103 | * [Task Lists](sections/tasklists.md) 104 | * [Task Recurring Profiles](sections/task_recurring_profiles.md) 105 | * [Tasks](sections/tasks.md) 106 | * [Time Entries](sections/entries.md) 107 | * [Users](sections/users.md) 108 | * [Task Assignments](sections/users_tasks.md) 109 | * [Workflows](sections/workflows.md) 110 | * [Workflow Statuses](sections/workflow_statuses.md) 111 | 112 | 113 | ## Data formats 114 | 115 | * [Date and time values](sections/datetime.md) 116 | 117 | For a sample and an explanation of each endpoint object, see individual endpoint pages. 118 | 119 | 120 | ## Filtering 121 | 122 | If you want to filter the response of listings, you can do so by supplying the `where` parameter in the request URL. 123 | 124 | Read more about [response filtering](sections/filtering.md) 125 | 126 | 127 | ## Including related content 128 | 129 | If you want a response to include additional information about an object, you can do so by supplying the `include` or `partial_include` parameters in the request URL. 130 | 131 | Read more about [additional includes](sections/includes.md) 132 | 133 | ## Webhooks 134 | 135 | Webhooks allow for 3rd party integrations. 136 | 137 | By creating a webhook you create a link between an event in Paymo (e.g. adding a task) and a URL that will be notified by Paymo when the event occurs. 138 | 139 | Read more about [webhooks](sections/webhooks.md) 140 | 141 | ## Help us make it better 142 | 143 | Please tell us how we can make the API better. If you have a specific feature request or if you found a bug, please use GitHub issues. Fork these docs and send a pull request with improvements. 144 | -------------------------------------------------------------------------------- /sections/subtasks.md: -------------------------------------------------------------------------------- 1 | # Subtasks 2 | 3 | * [Getting subtasks](#list) 4 | * [Getting a subtask](#get) 5 | * [Creating a subtask](#create) 6 | * [Updating a subtask](#update) 7 | * [Changing the order of subtasks](#update-subtasks-order) 8 | * [Deleting a subtask](#delete) 9 | * [The subtask object](#object) 10 | * [Dependent objects](#dependencies) 11 | 12 | 13 | ## Getting subtasks 14 | 15 | You can list subtasks by making a GET request to: 16 | 17 | * `/api/subtasks` for a list of all subtask from all projects 18 | * `/api/subtasks?where=task_id=[TASK_ID]` for a list of subtask from a task 19 | * `/api/subtasks?where=complete=false and task_id=[TASK_ID]` for a list of incomplete subtasks from a task 20 | 21 | Example of response: 22 | 23 | ```json 24 | { 25 | "subtasks": [ 26 | { 27 | "id": 1, 28 | "name": "Review design", 29 | "complete": true, 30 | "project_id": 4, 31 | "user_id": 1, 32 | "task_id": 7, 33 | "completed_on": "2020-04-22T06:54:52Z", 34 | "completed_by": 1, 35 | "created_on": "2020-04-22T06:50:55Z", 36 | "updated_on": "2020-04-22T06:54:52Z", 37 | "seq": 0 38 | }, 39 | { 40 | "id": 2, 41 | "name": "Add new features", 42 | "complete": false, 43 | "project_id": 4, 44 | "user_id": 1, 45 | "task_id": 7, 46 | "completed_on": null, 47 | "completed_by": null, 48 | "created_on": "2020-04-22T06:53:59Z", 49 | "updated_on": "2020-04-22T06:53:59Z", 50 | "seq": 1 51 | }, 52 | { 53 | "id": 3, 54 | "name": "Present design to the team", 55 | "complete": false, 56 | "project_id": 4, 57 | "user_id": 1, 58 | "task_id": 7, 59 | "completed_on": null, 60 | "completed_by": null, 61 | "created_on": "2020-04-22T06:54:13Z", 62 | "updated_on": "2020-04-22T06:54:50Z", 63 | "seq": 2 64 | } 65 | ] 66 | } 67 | ``` 68 | 69 | You can also [include related content](includes.md) when listing subtasks. 70 | 71 | 72 | ## Getting a subtask 73 | 74 | To get the subtask info, make a GET request to: 75 | 76 | * `/api/subtasks/[SUBTASK_ID]` 77 | 78 | Example response: 79 | 80 | ```json 81 | { 82 | "subtasks": [ 83 | { 84 | "id": 3, 85 | "name": "Present design to the team", 86 | "complete": false, 87 | "project_id": 4, 88 | "user_id": 1, 89 | "task_id": 7, 90 | "completed_on": null, 91 | "completed_by": null, 92 | "created_on": "2020-04-22T06:54:13Z", 93 | "updated_on": "2020-04-22T06:54:50Z", 94 | "seq": 2 95 | } 96 | ] 97 | } 98 | ``` 99 | 100 | You can also [include related content](includes.md) when getting a subtasks. 101 | 102 | 103 | ## Creating a subtask 104 | 105 | To create a subtask, make a POST request to: 106 | 107 | * `/api/subtasks` 108 | 109 | with the request body containing the new subtask info, as in the example below: 110 | 111 | ```json 112 | { 113 | "name": "Logo Design", 114 | "task_id": 546, 115 | } 116 | ``` 117 | 118 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new subtask. The response body will contain the new subtask info as in the **Getting a subtask** section. 119 | 120 | ### Required fields 121 | 122 | When creating a subtask: `name`, `task_id` 123 | 124 | 125 | ## Updating a subtask 126 | 127 | To update an existing subtask, make a POST or PUT request to: 128 | 129 | * `/api/subtasks/[SUBTASK_ID]` 130 | 131 | with the request body containing the updated info. You can send only the changed fields. 132 | 133 | Example of request body if you want to change the subtask name: 134 | 135 | ```json 136 | { 137 | "name": "Changes to logo" 138 | } 139 | ``` 140 | 141 | The response will return `200 OK` and will contain the updated subtask info as in the **Getting a subtask** section. 142 | 143 | 144 | ## Changing the order of subtasks 145 | 146 | To reorder the subtasks in a subtask list you need to make an **[update task](tasks.md#update-subtasks-order)** request with a body similar to: 147 | 148 | ```json 149 | { 150 | "subtasks_order": [ 39, 2, 10, 9, 11 ] 151 | } 152 | ``` 153 | 154 | where `subtasks_order` is a list of subtask ids (from the list of subtasks from a task) in the new order. 155 | 156 | You can change the order of a subset of subtasks by sending only the list of subtask ids that changed their position. 157 | 158 | 159 | ## Deleting a subtask 160 | 161 | To delete a subtask, make a DELETE request to: 162 | 163 | * `/api/subtasks/[SUBTASK_ID]` 164 | 165 | If successful, the response will have a `200 OK` status code. 166 | 167 | 168 | ## The subtask object 169 | 170 | A subtask object has the following attributes: 171 | 172 | Attribute|Type|Description 173 | ---------|----|----------- 174 | id | integer | _(read-only)_ Unique subtask identifier 175 | name | text | Task name 176 | complete | boolean | If `true` the subtask is marked as complete 177 | project_id | integer | _(read-only)_ Project id 178 | user_id | integer | Id of the user who created the subtask 179 | task_id | integer | Id of the task 180 | seq | integer | Position (order) of the subtask in the subtask list 181 | completed_on | integer | _(read-only)_ Date and time when the subtask was completed 182 | completed_by | integer | _(read-only)_ Id of the user that completed the subtask 183 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the subtask was created 184 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the subtask was last updated 185 | 186 | 187 | ## Dependent objects 188 | 189 | The following object types can be used in [includes](includes.md): 190 | 191 | Object type|Include key|Relationship 192 | -----------|-----------|---- 193 | [Project](projects.md) | project | parent 194 | [Task](tasks.md) | task | parent 195 | [User](users.md) | user | parent 196 | -------------------------------------------------------------------------------- /sections/company.md: -------------------------------------------------------------------------------- 1 | # Company 2 | 3 | * [Getting company info](#get) 4 | * [Updating company](#update) 5 | * [Adding a company logo](#add-image) 6 | * [The company object](#object) 7 | 8 | 9 | ## Getting company info 10 | 11 | You can get the company info along with the settings for the company by making a GET request to: 12 | 13 | * `/api/company` 14 | 15 | Example response: 16 | 17 | ```json 18 | { 19 | "company": { 20 | "id": 123, 21 | "name": "Dunder Mifflin Paper Company", 22 | "address": "Academy St, Scranton, PA 18504", 23 | "phone": "+015641789891", 24 | "email": "michael@dundermifflin.com", 25 | "url": "www.dundermifflin.com", 26 | "fiscal_information": "", 27 | "country": "US", 28 | "image": "https:\/\/app.paymoapp.com\/assets\/123\/ec4e71d560281f6d55b8c24c7f42db2f.png", 29 | "active": true, 30 | "account_type": "commercial", 31 | "created_on": "2014-10-03T12:49:05Z", 32 | "updated_on": "2014-12-03T12:55:49Z", 33 | "apply_tax_to_expenses": "1", 34 | "date_format": "m/d/Y", 35 | "default_currency": "USD", 36 | "default_price_per_hour": "30", 37 | "default_tax": "0", 38 | "default_tax2": "0", 39 | "default_tax2_text": "Tax2", 40 | "default_tax_text": "V.A.T.", 41 | "due_interval": "30", 42 | "estimate_format": "#EST-[yyyy][mm][dd]-[i]", 43 | "hide_tax_field": "", 44 | "invoice_format": "#INV-[yyyy][mm][dd]-[i]", 45 | "language": "en", 46 | "next_estimate_number": "101", 47 | "next_invoice_number": "201", 48 | "payment_reminder_1": "0", 49 | "payment_reminder_2": "0", 50 | "payment_reminder_3": "0", 51 | "remove_paymo_branding": "", 52 | "tax_on_tax": "0", 53 | "timezone": "US/Eastern", 54 | "time_format": "h:i a", 55 | "week_start": "1", 56 | "workday_start": "09:00", 57 | "workday_end": "17:00", 58 | "working_days": "1,2,3,4,5", 59 | "decimal_sep": ".", 60 | "thousands_sep": ",", 61 | "online_payments": true, 62 | "max_users": 4, 63 | "current_users": 2, 64 | "max_projects": null, 65 | "current_projects": 6, 66 | "max_invoices": null, 67 | "current_invoices": 0 68 | } 69 | } 70 | ``` 71 | 72 | 73 | ## Updating company 74 | 75 | To update the company info or add/modify a company setting, make a POST or PUT request to: 76 | 77 | * `/api/company` 78 | 79 | with the request body containing the updated info. You can send only the changed fields. 80 | 81 | Example of request body if you want to change the company name: 82 | 83 | ```json 84 | { 85 | "name": "Our new company name" 86 | } 87 | ``` 88 | 89 | **Note**: Not all the attributes of a company can be changed. 90 | 91 | 92 | ## Adding a company logo 93 | 94 | To add a company logo image, make a POST request to: 95 | 96 | * `/api/company` 97 | 98 | The request content-type should be `multipart-form-data` and the file field name equal to `image`. Here's an example using `curl` command line: 99 | 100 | ```shell 101 | curl -u email:password 102 | -H 'Accept: application/json' 103 | -F "image=@logo.png" 104 | https://app.paymoapp.com/api/company 105 | ``` 106 | 107 | Accepted image file formats are: JPEG, PNG, GIF. 108 | 109 | 110 | ## The company object 111 | 112 | Attribute|Type|Description 113 | ------|------|----- 114 | id | integer | _(read-only)_ Unique company identifier 115 | name | text | Company name 116 | address | text | Company address 117 | phone | text | Company phone number 118 | email | email | Company email address. This is the main email address where notifications from Paymo are send. It is also the email address that was used at sign-up and is the email address of the first user. 119 | url | url | Company website 120 | fiscal_information | text | Fiscal information (used in invoice headers) 121 | country | text | Company country code in [ISO 3166-1 alpha-2](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) format 122 | image | url | Company logo image URL 123 | image_thumb_large | url | _(read-only)_ Company logo large size thumbnail URL 124 | image_thumb_medium | url | _(read-only)_ Company logo medium size thumbnail URL 125 | image_thumb_small | url | _(read-only)_ Company logo small size thumbnail URL 126 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the project was created 127 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the project was last updated 128 | timezone | text | Default company timezone. This timezone value will be used by default when viewing invoices, reports, estimates in permalinks. [List of available options](http://en.wikipedia.org/wiki/List_of_tz_database_time_zones) 129 | default_currency | text | Default currency for the company. [List of available currencies](currencies.md) 130 | default_price_per_hour | text | Default price per hour. It is used as the default value for user price per hour, when adding new users. 131 | apply_tax_to_expenses | text | If equal to `1` will apply tax to expenses when creating new invoices. 132 | tax_on_tax | text | Default value for invoice `tax_on_tax` when creating new invoices. 133 | currency_position | text | Position of currency relative to displayed amounts. Available options: `left`, `right`. 134 | next_invoice_number | text | Autoincrement number value when creating new invoice 135 | next_estimate_number | text | Autoincrement number value when creating new estimate 136 | online_payments | text | If equal to `1`, online payment for invoices will be enabled. 137 | date_format | text | Format for displaying dates in the application. Available options: `Y-m-d`, `d/m/Y`, `m/d/Y`, `d.m.Y`. 138 | time_format | text | Format for displaying time values. Available options: `H:i` for 24-hour format, `h:i a` for 12-hour format. 139 | decimal_sep | text | Decimal separator for displaying numeric values 140 | thousands_sep | text | Thousands separator for displaying numeric values 141 | week_start | text | Numeric value in the range 0-6 representing the day the week starts, 0 being Sunday, 6 being Saturday. 142 | workday_start | text | Workday start hour in HH:SS 24-hour format 143 | workday_end | text | Workday end hour in HH:SS 24-hour format 144 | working_days | text | Comma separated list of working days of the week. Days are represented by numbers in the range 0-6, 0 being Sunday, 6 being Saturday. 145 | account_type | text | _(read-only)_ Type of Paymo account: free or commercial 146 | max_users | integer | _(read-only)_ Maximal number of active users for the company, as set up in the Paymo subscription 147 | current_users | integer | _(read-only)_ Number of active users in the company 148 | max_projects | integer | _(read-only)_ Maximal number of active projects for the company. If `null`, the limit does not apply. 149 | current_projects | integer | _(read-only)_ Number of active projects in the company 150 | max_invoices | integer | _(read-only)_ Maximal number of invoices that can be created this month, as set up in the Paymo subscription. If `null`, the limit does not apply. 151 | current_invoices | integer | _(read-only)_ Number of invoices created this month 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /sections/clients.md: -------------------------------------------------------------------------------- 1 | # Clients 2 | 3 | * [Getting clients](#list) 4 | * [Getting a client](#get) 5 | * [Creating a client](#create) 6 | * [Updating a client](#update) 7 | * [Archiving or activating a client](#archive) 8 | * [Adding a client logo](#add-image) 9 | * [Deleting a client](#delete) 10 | * [Client object](#object) 11 | * [Dependent objects](#dependecies) 12 | 13 | 14 | ## Getting clients 15 | 16 | You can list clients by making a GET request to: 17 | 18 | * `/api/clients` for a list of all clients 19 | * `/api/clients?where=active=true` for a list of active clients 20 | * `/api/clients?where=active=false` for a list of archived clients 21 | 22 | Example of response: 23 | 24 | ```json 25 | { 26 | "clients": [ 27 | { 28 | "id": 1, 29 | "name": "NBC Universal, Inc.", 30 | "address": "1277 Fairfax Drive", 31 | "city": "New York", 32 | "state": "NY", 33 | "postal_code": "07302", 34 | "country": "US", 35 | "phone": "646-772-0207", 36 | "fax": "646-772-0207", 37 | "email": "sales@nbc.com", 38 | "website": "www.nbc.com", 39 | "image": "https://app.paymoapp.com/assets/1/clients/fd1fc0c388bbd7aff03a6be5aa5f8945.png", 40 | "fiscal_information": "", 41 | "active": true, 42 | "created_on": "2013-06-26T12:07:44Z", 43 | "updated_on": "2014-08-04T12:12:13Z" 44 | }, 45 | { 46 | "id": 2, 47 | "name": "Best Buy Co., Inc.", 48 | "address": "4965 North Street\n", 49 | "city": "Richfield", 50 | "state": "Minnesota", 51 | "postal_code": "84116", 52 | "country": "US", 53 | "phone": "716-592-1227", 54 | "fax": "716-592-1227", 55 | "email": "sales@bestbuy.com", 56 | "website": "www.bestbuy.com", 57 | "image": "https://app.paymoapp.com/assets/1/clients/103bcf5e11fa4f261151e2a3b69be269.png", 58 | "fiscal_information": "", 59 | "active": true, 60 | "created_on": "2013-06-26T12:07:44Z", 61 | "updated_on": "2014-08-13T12:35:48Z" 62 | } 63 | ] 64 | } 65 | ``` 66 | 67 | You can also [include related content](includes.md) when listing clients. 68 | 69 | 70 | ## Getting a client 71 | 72 | To get the client info, make a GET request to: 73 | 74 | * `/api/clients/[CLIENT_ID]` 75 | 76 | Example response: 77 | 78 | ```json 79 | { 80 | "clients": [ 81 | { 82 | "id": 1, 83 | "name": "NBC Universal, Inc.", 84 | "address": "1277 Fairfax Drive", 85 | "city": "New York", 86 | "state": "NY", 87 | "postal_code": "07302", 88 | "country": "US", 89 | "phone": "646-772-0207", 90 | "fax": "646-772-0207", 91 | "email": "sales@nbc.com", 92 | "website": "www.nbc.com", 93 | "image": "https://app.paymoapp.com/assets/1/clients/fd1fc0c388bbd7aff03a6be5aa5f8945.png", 94 | "fiscal_information": "", 95 | "active": true, 96 | "created_on": "2013-06-26T12:07:44Z", 97 | "updated_on": "2014-08-04T12:12:13Z" 98 | } 99 | ] 100 | } 101 | ``` 102 | 103 | You can also [include related content](includes.md) when getting a client. 104 | 105 | 106 | ## Creating a client 107 | 108 | To create a client, make a POST request to: 109 | 110 | * `/api/clients` 111 | 112 | with the request body containing the new client info, as in the example below: 113 | 114 | ```json 115 | { 116 | "name": "Smith and Sons", 117 | "email": "office@smithandsons.com" 118 | } 119 | ``` 120 | 121 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new client. The response body will contain the new client info as in the **Getting a client** section. 122 | 123 | ### Required fields 124 | 125 | When creating a client: `name`. 126 | 127 | 128 | ## Updating a client 129 | 130 | To update an existing client, make a POST or PUT request to: 131 | 132 | * `/api/clients/[CLIENT_ID]` 133 | 134 | with the request body containing the updated info. You can send only the changed fields. 135 | 136 | Example of request body if you want to change the client name and address: 137 | 138 | ```json 139 | { 140 | "name": "Smith and Sons Ltd.", 141 | "address": "2000 Salmon Creek Lane\nJuneau, AK 99801" 142 | } 143 | ``` 144 | 145 | The response will return `200 OK` and will contain the updated client info as in the **Getting a client** section. 146 | 147 | 148 | ## Archiving or activating a client 149 | 150 | To archive a client, make an update request with the following body: 151 | 152 | ```json 153 | { 154 | "active": false 155 | } 156 | ``` 157 | 158 | To activate, send a `true` value. 159 | 160 | 161 | ## Adding a client logo 162 | 163 | To add a client logo image, make a POST request to: 164 | 165 | * `/api/clients/[CLIENT_ID]` 166 | 167 | The request content-type should be `multipart-form-data` and the file field name equal to `image`. Here's an example using `curl` command line: 168 | 169 | ```shell 170 | curl -u email:password 171 | -H 'Accept: application/json' 172 | -F "image=@logo.png" 173 | https://app.paymoapp.com/api/clients/12345 174 | ``` 175 | 176 | Accepted image file formats are: JPEG, PNG, GIF. 177 | 178 | The logo image of a client can be added when creating the client. In that case, all the client fields should be send in `multipart-form-data` format together with the file. 179 | 180 | 181 | ## Deleting a client 182 | 183 | To delete a client, make a DELETE request to: 184 | 185 | * `/api/clients/[CLIENT_ID]` 186 | 187 | If successful, the response will have a `200 OK` status code. 188 | 189 | ### Warning 190 | 191 | **Deleting a client will also delete all info related to that client: projects, task and task lists, time entries from those projects!** 192 | 193 | 194 | ## The client object 195 | 196 | A client object has the following attributes: 197 | 198 | Attribute|Type|Description 199 | ---------|----|----------- 200 | id | integer | _(read-only)_ Unique client identifier 201 | name | text | Client name 202 | address | text | Street address 203 | city | text | City name 204 | postal_code | text | Postal code 205 | country | text | Country name 206 | state | text | State or Region name 207 | phone | text | Phone number 208 | fax | text | Fax number 209 | email | email | Email address 210 | website | url | Website address 211 | active | boolean | _(read-only)_ If `true` the client is active, otherwise the client is archived. 212 | fiscal_information | text | Fiscal information. It is used in invoice headers to display client details. 213 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the client was created 214 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the client was last updated 215 | image | url | Client logo image URL 216 | image_thumb_large | url | _(read-only)_ Client logo image large size thumbnail URL 217 | image_thumb_medium | url | _(read-only)_ Client logo image medium size thumbnail URL 218 | image_thumb_small | url | _(read-only)_ Client logo image small size thumbnail URL 219 | 220 | 221 | ## Dependent objects 222 | 223 | The following object types can be used in [includes](includes.md): 224 | 225 | Object type|Include key|Relationship 226 | -----------|-----------|---- 227 | [Client contact](client_contacts.md) | clientcontacts | child 228 | [Project](projects.md) | projects | child 229 | [Invoice](invoices.md) | invoices | child 230 | [Recurring invoice profile](recurring_profiles.md) | recurringprofiles | child 231 | -------------------------------------------------------------------------------- /sections/client_contacts.md: -------------------------------------------------------------------------------- 1 | # Client contacts 2 | 3 | * [Getting client contacts](#list) 4 | * [Getting a client contact](#get) 5 | * [Creating a client contact](#create) 6 | * [Updating a client contact](#update) 7 | * [Giving access to clients portal](#access) 8 | * [Adding a client contact profile image](#add-image) 9 | * [Deleting a client contact](#delete) 10 | * [The client contact object](#object) 11 | * [Dependent objects](#dependencies) 12 | 13 | 14 | ## Getting client contacts 15 | 16 | You can list client contacts by making a GET request to: 17 | 18 | * `/api/clientcontacts` for a list of all contacts for all clients 19 | * `/api/clientcontacts?where=client_id=[CLIENT_ID]` for a list of all contacts for a specific client 20 | 21 | Example of response: 22 | 23 | ```json 24 | { 25 | "clientcontacts": [ 26 | { 27 | "id": 1, 28 | "client_id": 1, 29 | "name": "Aaron Cooper", 30 | "email": "aaron@stonecoppergrandy.com", 31 | "mobile": "630-350-5512", 32 | "phone": "361-613-5188", 33 | "fax": "361-613-5188", 34 | "skype": "Aaron.Cooper", 35 | "notes": null, 36 | "image": "https://app.paymoapp.com/assets/1/clients/1/contacts/7ce81eab305059b0484e439101533b1f.jpg", 37 | "is_main": true, 38 | "position": "Associate", 39 | "access": false, 40 | "created_on": "2013-07-24T11:52:44Z", 41 | "updated_on": "2014-08-13T13:00:24Z" 42 | }, 43 | { 44 | "id": 2, 45 | "client_id": 2, 46 | "name": "Christian Meadows", 47 | "email": "christian@lackwannacounty.com", 48 | "mobile": "213-350-4605", 49 | "phone": "862-778-3343", 50 | "fax": "862-778-3343", 51 | "skype": "Christian.Meadows", 52 | "notes": null, 53 | "image": "https://app.paymoapp.com/assets/2/clients/2/contacts/ac5ec4905a07b2c9275045d49e824cb7.jpg", 54 | "is_main": true, 55 | "position": "CEO", 56 | "access": false, 57 | "created_on": "2013-07-24T11:52:44Z", 58 | "updated_on": "2014-08-07T09:23:15Z" 59 | } 60 | ] 61 | } 62 | ``` 63 | 64 | You can also [include related content](includes.md) when listing client contacts. 65 | 66 | 67 | ## Getting a client contact 68 | 69 | To get the client contact info, make a GET request to: 70 | 71 | * `/api/clientcontacts/[CONTACT_ID]` 72 | 73 | Example response: 74 | 75 | ```json 76 | { 77 | "clientcontacts": [ 78 | { 79 | "id": 1, 80 | "client_id": 1, 81 | "name": "Aaron Cooper", 82 | "email": "aaron@stonecoppergrandy.com", 83 | "mobile": "630-350-5512", 84 | "phone": "361-613-5188", 85 | "fax": "361-613-5188", 86 | "skype": "Aaron.Cooper", 87 | "notes": null, 88 | "image": "https://app.paymoapp.com/assets/1/clients/1/contacts/7ce81eab305059b0484e439101533b1f.jpg", 89 | "is_main": true, 90 | "position": "Associate", 91 | "access": false, 92 | "created_on": "2013-07-24T11:52:44Z", 93 | "updated_on": "2014-08-13T13:00:24Z" 94 | } 95 | ] 96 | } 97 | ``` 98 | 99 | You can also [include related content](includes.md) when getting a client contact. 100 | 101 | 102 | ## Creating a client contact 103 | 104 | To create a client contact, make a POST request to: 105 | 106 | * `/api/clientcontacts` 107 | 108 | with the request body containing the new contact info, as in the example below: 109 | 110 | ```json 111 | { 112 | "name": "James Mick", 113 | "client_id": 1, 114 | "email": "james@ea.com", 115 | "phone": "577-293-1857" 116 | } 117 | ``` 118 | 119 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new client. The response body will contain the new client info as in the **Getting a client** section. 120 | 121 | ### Required fields 122 | 123 | When creating a client: `name`, `client_id`. 124 | 125 | 126 | ## Updating a client contact 127 | 128 | To update an existing client contact, make a POST or PUT request to: 129 | 130 | * `/api/clientcontacts/[CONTACT_ID]` 131 | 132 | with the request body containing the updated info. You can send only the changed fields. 133 | 134 | Example of request body if you want to change the contact name and phone number: 135 | 136 | ```json 137 | { 138 | "name": "James Mick Jr.", 139 | "phone": "578-293-1857" 140 | } 141 | ``` 142 | 143 | The response will return `200 OK` and will contain the updated client contact info as in the **Getting a client contact** section. 144 | 145 | 146 | ## Giving access to clients portal 147 | 148 | To give a client contact access to the clients portal, make an update request with the following body: 149 | 150 | ```json 151 | { 152 | "access": true, 153 | "password": "secret" 154 | } 155 | ``` 156 | 157 | The client contact will be able to login into clients portal using his email and provided password. 158 | 159 | To remove access, make the update request with the following body: 160 | 161 | ```json 162 | { 163 | "access": false 164 | } 165 | ``` 166 | 167 | 168 | ## Adding a client contact profile image 169 | 170 | To add a client contact profile image, make a POST request to: 171 | 172 | * `/api/clientcontacts/[CONTACT_ID]` 173 | 174 | The request content-type should be `multipart-form-data` and the file field name equal to `image`. Here's an example using `curl` command line: 175 | 176 | ```shell 177 | curl -u email:password 178 | -H 'Accept: application/json' 179 | -F "image=@photo.jpg" 180 | https://app.paymoapp.com/api/clientcontacts/12345 181 | ``` 182 | 183 | Accepted image file formats are: JPEG, PNG, GIF. 184 | 185 | The profile image of a client contact can be added when creating the client contact. In that case, all the client contact fields should be send in `multipart-form-data` format together with the file. 186 | 187 | 188 | ## Deleting a client contact 189 | 190 | To delete a client contact, make a DELETE request to: 191 | 192 | * `/api/clientcontacts/[CONTACT_ID]` 193 | 194 | If successful, the response will have a `200 OK` status code. 195 | 196 | 197 | ## The client contact object 198 | 199 | A client contact object has the following attributes: 200 | 201 | Attribute|Type|Description 202 | ---------|----|----------- 203 | id | integer | _(read-only)_ Unique client contact identifier 204 | name | text | Client name 205 | email | email | Email address 206 | mobile | text | Mobile phone number 207 | phone | text | Phone number 208 | fax | text | Fax number 209 | skype | text | Skype account name 210 | notes | text | Notes 211 | position | text | Job position description 212 | is_main | boolean | If `true` the contact is the main contact for this client. 213 | access | boolean | If `true` the contact has access to the client portal. 214 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the contact was created 215 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the contact was last updated 216 | image | url | Profile image URL 217 | image_thumb_large | url | _(read-only)_ Profile image large size thumbnail URL 218 | image_thumb_medium | url | _(read-only)_ Profile image medium size thumbnail URL 219 | image_thumb_small | url | _(read-only)_ Profile image small size thumbnail URL 220 | 221 | 222 | ## Dependent objects 223 | 224 | The following object types can be used in [includes](includes.md): 225 | 226 | Object type|Include key|Relationship 227 | -----------|-----------|-------- 228 | [Client](clients.md) | client | parent 229 | -------------------------------------------------------------------------------- /sections/expenses.md: -------------------------------------------------------------------------------- 1 | # Expenses 2 | 3 | * [Getting expenses](#list) 4 | * [Getting an expense](#get) 5 | * [Creating an expense](#create) 6 | * [Updating an expense](#update) 7 | * [Invoicing an expense](#invoicing) 8 | * [Attaching a receipt to an expense](#attach-receipt) 9 | * [Deleting an expense](#delete) 10 | * [The expense object](#object) 11 | * [Dependent objects](#dependencies) 12 | 13 | 14 | ## Getting expenses 15 | 16 | You can list expenses by making a GET request to: 17 | 18 | * `/api/expenses` for a list of all the expenses 19 | * `/api/expenses?where=client_id=[CLIENT_ID]` for a list of all the expenses from a client 20 | * `/api/expenses?where=project_id=[PROJECT_ID]` for a list of all the expenses from a project 21 | * `/api/expenses?where=date>=2014-09-01` for a list of expenses added after Sep 1st, 2014 22 | * `/api/expenses?where=invoiced=false` for a list of all expenses not yet invoiced 23 | 24 | Example of response: 25 | 26 | ```json 27 | { 28 | "expenses":[ 29 | { 30 | "id":1259, 31 | "client_id":1259, 32 | "project_id":null, 33 | "user_id":3180, 34 | "amount":223.26, 35 | "currency":"USD", 36 | "date":"2013-03-28", 37 | "notes":"Travel to Boston", 38 | "invoiced":false, 39 | "invoice_item_id":null, 40 | "file":"", 41 | "created_on":"2013-03-28T15:06:28Z", 42 | "updated_on":"2013-03-28T15:06:28Z", 43 | "tags":[ 44 | "Mileage" 45 | ] 46 | }, 47 | { 48 | "id":1261, 49 | "client_id":1261, 50 | "project_id":null, 51 | "user_id":3180, 52 | "amount":9.45, 53 | "currency":"USD", 54 | "date":"2013-01-04", 55 | "notes":"Meeting with client", 56 | "invoiced":true, 57 | "invoice_item_id":7810, 58 | "file":"", 59 | "created_on":"2015-03-05T20:09:17Z", 60 | "updated_on":"2015-03-05T20:52:42Z", 61 | "tags":[ 62 | "Restaurant" 63 | ] 64 | } 65 | ] 66 | } 67 | ``` 68 | 69 | You can also [include related content](includes.md) when listing expenses. 70 | 71 | 72 | ## Getting an expense 73 | 74 | To get the expense info, make a GET request to: 75 | 76 | * `/api/expenses/[EXPENSE_ID]` 77 | 78 | Example response: 79 | 80 | ```json 81 | { 82 | "expenses":[ 83 | { 84 | "id":1259, 85 | "client_id":1259, 86 | "project_id":null, 87 | "user_id":3180, 88 | "amount":223.26, 89 | "currency":"USD", 90 | "date":"2013-03-28", 91 | "notes":"Travel to Boston", 92 | "invoiced":false, 93 | "invoice_item_id":null, 94 | "file":"", 95 | "created_on":"2013-03-28T15:06:28Z", 96 | "updated_on":"2013-03-28T15:06:28Z", 97 | "tags":[ 98 | "Mileage" 99 | ] 100 | } 101 | ] 102 | } 103 | ``` 104 | 105 | You can also [include related content](includes.md) when getting an expense. 106 | 107 | 108 | ## Creating an expense 109 | 110 | To create an expense, make a POST request to: 111 | 112 | * `/api/expenses` 113 | 114 | with the request body containing the new expense info, as in the example below: 115 | 116 | ```json 117 | { 118 | "client_id": 1261, 119 | "user_id": 318, 120 | "amount": 29.95, 121 | "currency": "USD", 122 | "date": "2013-02-14", 123 | "notes": "Hosting", 124 | "tags": [ 125 | "Software/Service" 126 | ] 127 | } 128 | ``` 129 | 130 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new expense. The response body will contain the new expense info as in the **Getting an expense** section. 131 | 132 | ### Required fields 133 | 134 | When creating an expense: `client_id`, `currency`, `amount`. 135 | 136 | 137 | ## Updating an expense 138 | 139 | To update an existing expense, make a POST or PUT request to: 140 | 141 | * `/api/expenses/[EXPENSE_ID]` 142 | 143 | with the request body containing the updated info. You can send only the changed fields. 144 | 145 | Example of request body if you want to change the expense date: 146 | 147 | ```json 148 | { 149 | "date": "2015-02-15" 150 | } 151 | ``` 152 | 153 | The response will return `200 OK` and will contain the updated expense info as in the **Getting an expense** section. 154 | 155 | 156 | ## Invoicing an expense 157 | 158 | To invoice an expense, you have to create an invoice with the expense as an invoice line item. For example: 159 | 160 | ```json 161 | { 162 | "client_id":1261, 163 | "currency":"USD", 164 | "date":"2013-08-14", 165 | "items":[ 166 | { 167 | "item":"Expense", 168 | "expense_id":100545 169 | } 170 | ] 171 | } 172 | ``` 173 | If successful, the expense will be updated with `invoiced`=`true` and `invoice_item_id` will contain the ID of the invoice line item for the expense. 174 | 175 | Alternatively, to mark an expense as invoiced without creating an invoice, make an update request with the following body: 176 | 177 | ```json 178 | { 179 | "invoiced": true 180 | } 181 | ``` 182 | 183 | 184 | ## Attaching a receipt to an expense 185 | 186 | To add a receipt to an existing expense, make a POST request to: 187 | 188 | * `/api/expenses/[EXPENSE_ID]` 189 | 190 | The request content-type should be `multipart-form-data` and the file field name equal to `file`. Here's an example using `curl` command line: 191 | 192 | ```shell 193 | curl -u email:password 194 | -H 'Accept: application/json' 195 | -F "file=@receipt.jpg" 196 | https://app.paymoapp.com/api/expenses/1261 197 | ``` 198 | 199 | The receipt for an expense can also be added when creating the expense. In that case, all the expense fields should be send in `multipart-form-data` format together with the file. 200 | 201 | If the receipt file is an image, the expense object representation will contain additional fields for thumbnails. 202 | 203 | 204 | ## Deleting an expense 205 | 206 | To delete an expense, make a DELETE request to: 207 | 208 | * `/api/expenses/[EXPENSE_ID]` 209 | 210 | If successful, the response will have a `200 OK` status code. 211 | 212 | 213 | ## The expense object 214 | 215 | An expense object has the following attributes: 216 | 217 | Attribute|Type|Description 218 | ---------|----|----------- 219 | id | integer | _(read-only)_ Unique expense identifier 220 | client_id | integer | Client id 221 | project_id | integer | Project id 222 | user_id | integer | Id of the user who added the expense. 223 | amount | decimal | Expense amount 224 | currency | text | Expense currency code. See the [list of currencies](currencies.md). 225 | date | [date](datetime.md) | Date for the expense 226 | notes | text | Expense notes or description 227 | invoiced | boolean | If `true` the expense is marked as invoiced. 228 | invoice_item_id | integer | Id of the invoice line item when the expense was invoiced. 229 | tags | list | List of tags for the expense. 230 | file | url | Receipt file URL 231 | image_thumb_large | url | _(read-only)_ Receipt large size thumbnail URL. _(Only for images)_ 232 | image_thumb_medium | url | _(read-only)_ Receipt medium size thumbnail URL. _(Only for images)_ 233 | image_thumb_small | url | _(read-only)_ Receipt small size thumbnail URL. _(Only for images)_ 234 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the expense was created 235 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the expense was last updated 236 | 237 | 238 | ## Dependent objects 239 | 240 | The following object types can be used in [includes](includes.md): 241 | 242 | Object type|Include key|Relationship 243 | -----------|-----------|---- 244 | [Client](clients.md) | client | parent 245 | [Project](projects.md) | project | parent 246 | [User](users.md) | user | parent 247 | [Invoice Item](invoices.md#item-object) | invoiceitems | child 248 | -------------------------------------------------------------------------------- /sections/comments.md: -------------------------------------------------------------------------------- 1 | # Comments 2 | 3 | * [Getting comments](#list) 4 | * [Getting a comment](#get) 5 | * [Creating a comment](#create) 6 | * [Updating a comment](#update) 7 | * [Attaching a file to a comment](#add-file) 8 | * [Deleting a comment](#delete) 9 | * [The comment object](#object) 10 | * [Dependent objects](#dependencies) 11 | 12 | In Paymo you can comment on discussions, tasks and files. Each comment belongs to a comments thread. 13 | 14 | 15 | ## Getting comments 16 | 17 | You can list comments by making a GET request to: 18 | 19 | * `/api/threads?where=project_id=[PROJECT_ID]` for a list of comment threads from a project (may be related to a discussion, a task or a file) 20 | * `/api/threads?where=task_id=[TASK_ID]&include=comments` for a list of comments on a task 21 | * `/api/threads?where=discussion_id=[DISCUSSION_ID]&include=comments` for a list of comments on a discussion 22 | * `/api/threads?where=file_id=[FILE_ID]&include=comments` for a list of comments on a file 23 | * `/api/comments?where=thread_id=[THREAD_ID]` for a list of comments in a comments thread 24 | 25 | Example of response: 26 | 27 | ```json 28 | { 29 | "threads": [ 30 | { 31 | "id": 79885, 32 | "project_id": 397709, 33 | "task_id": 3419186, 34 | "created_on": "2015-01-26T15:06:06Z", 35 | "updated_on": "2015-01-26T15:06:17Z", 36 | "comments": [ 37 | { 38 | "id": 125236, 39 | "thread_id": 79885, 40 | "user_id": 23129, 41 | "content": "Please change the form submit button positioning as in the new design.", 42 | "created_on": "2015-01-26T15:06:06Z", 43 | "updated_on": "2015-01-26T15:06:06Z" 44 | }, 45 | { 46 | "id": 125237, 47 | "thread_id": 79885, 48 | "user_id": 23129, 49 | "content": "See the attached file for the new design.", 50 | "created_on": "2015-01-26T15:06:17Z", 51 | "updated_on": "2015-01-26T15:06:17Z" 52 | } 53 | ] 54 | } 55 | ] 56 | } 57 | ``` 58 | 59 | or 60 | 61 | ```json 62 | { 63 | "comments": [ 64 | { 65 | "id": 125234, 66 | "thread_id": 79884, 67 | "user_id": 23129, 68 | "content": "See this description", 69 | "created_on": "2015-01-26T14:51:41Z", 70 | "updated_on": "2015-01-26T14:51:41Z" 71 | }, 72 | { 73 | "id": 125235, 74 | "thread_id": 79884, 75 | "user_id": 23129, 76 | "content": "map", 77 | "created_on": "2015-01-26T14:52:04Z", 78 | "updated_on": "2015-01-26T14:52:04Z" 79 | } 80 | ] 81 | } 82 | ``` 83 | 84 | You can also [include related content](includes.md) when listing comments. 85 | 86 | 87 | ## Getting a comment 88 | 89 | To get the comment info, make a GET request to: 90 | 91 | * `/api/comments/[COMMENT_ID]` 92 | * `/api/comments/[COMMENT_ID]?include=files` for comment info with a list of attached files 93 | 94 | Example response: 95 | 96 | ```json 97 | { 98 | "comments": [ 99 | { 100 | "id": 125234, 101 | "thread_id": 79884, 102 | "user_id": 23129, 103 | "content": "See this description", 104 | "created_on": "2015-01-26T14:51:41Z", 105 | "updated_on": "2015-01-26T14:51:41Z" 106 | } 107 | ] 108 | } 109 | ``` 110 | 111 | You can also [include related content](includes.md) when getting a comment. 112 | 113 | 114 | ## Creating a comment 115 | 116 | To create a comment, make a POST request to: 117 | 118 | * `/api/comments` 119 | 120 | with the request body containing the new comment info. If you know the `thread_id` you can send it in the request body, or you can send the `discussion_id`, or `task_id`, or `file_id` if you are adding the comment to a discussion, or task, or file. 121 | 122 | An example of adding a comment to a task: 123 | 124 | ```json 125 | { 126 | "task_id": 12345, 127 | "content": "Please review my changes" 128 | } 129 | ``` 130 | 131 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new comment. The response body will contain the new comment info as in the **Getting a comment** section. 132 | 133 | ### Required fields 134 | 135 | When creating a comment: `content` and one field from the group (`thread_id`, `task_id`, `discussion_id`, `file_id`). 136 | 137 | 138 | ## Updating a comment 139 | 140 | To update an existing comment, make a POST or PUT request to: 141 | 142 | * `/api/comments/[COMMENT_ID]` 143 | 144 | with the request body containing the updated info. You can send only the changed fields. 145 | 146 | Example of request body if you want to change the comment due date: 147 | 148 | ```json 149 | { 150 | "content": "[Updated comment] Forget all I have said before" 151 | } 152 | ``` 153 | 154 | The response will return `200 OK` and will contain the updated comment info as in the **Getting a comment** section. 155 | 156 | 157 | ## Attaching a file to a comment 158 | 159 | To add a file to a task, make a POST request to: 160 | 161 | * `/api/comments/[COMMENT_ID]` 162 | 163 | The request content-type should be `multipart-form-data` and the file field name equal to `file`. Here's an example using `curl` command line: 164 | 165 | ```shell 166 | curl -u email:password 167 | -H 'Accept: application/json' 168 | -F "file=@screenshot.png" 169 | https://app.paymoapp.com/api/comments/12345 170 | ``` 171 | 172 | The file can also be added when creating the comment. In that case, all the comment fields should be send in `multipart-form-data` format together with the file. 173 | 174 | 175 | ## Deleting a comment 176 | 177 | To delete a comment, make a DELETE request to: 178 | 179 | * `/api/comments/[COMMENT_ID]` 180 | 181 | If successful, the response will have a `200 OK` status code. 182 | 183 | 184 | ## The comment object 185 | 186 | A comment object has the following attributes: 187 | 188 | Attribute|Type|Description 189 | ---------|----|----------- 190 | id | integer | _(read-only)_ Unique comment identifier 191 | content | html | Comment content 192 | thread_id | integer | _(read-only)_ Id of the comments thread the comment is part of 193 | user_id | integer | _(read-only)_ Id of the user that created the comment 194 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the comment was created 195 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the comment was last updated 196 | 197 | ## The thread object 198 | 199 | A comment thread object has the following attributes: 200 | 201 | Attribute|Type|Description 202 | ---------|----|----------- 203 | id | integer | _(read-only)_ Unique thread identifier 204 | project_id | integer | _(read-only)_ Id of the project the comments thread is part of 205 | discussion_id | integer | _(read-only)_ Id of the discussion this thread of comments if linked to (Only one of the following attributes exists for a thread: `discussion_id` or `task_id` or `file_id`) 206 | task_id | integer | _(read-only)_ Id of the task this thread of comments if linked to 207 | file_id | integer | _(read-only)_ Id of the file this thread of comments if linked to 208 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the comment was created 209 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the comment was last updated 210 | 211 | 212 | ## Dependent objects 213 | 214 | The following object types can be used in [includes](includes.md) for a comment: 215 | 216 | Object type|Include key|Relationship 217 | -----------|-----------|---- 218 | [Thread](comments.md) | thread | parent 219 | [User](users.md) | user | parent 220 | [Project](projects.md) | project | parent 221 | [Files](files.md) | files | child 222 | 223 | The following object types can be used in [includes](includes.md) for a comments thread: 224 | 225 | Object type|Include key|Relationship 226 | -----------|-----------|---- 227 | [Project](projects.md) | project | parent 228 | [Discussion](discussion.md) | discussion | parent 229 | [Task](tasks.md) | task | parent 230 | [Files](files.md) | file | parent 231 | [Comment](comments.md) | comments | child 232 | -------------------------------------------------------------------------------- /sections/entries.md: -------------------------------------------------------------------------------- 1 | # Time entries 2 | 3 | * [Getting time entries](#list) 4 | * [Getting a time entry](#get) 5 | * [Creating a time entry](#create) 6 | * [Updating a time entry](#update) 7 | * [Deleting a time entry](#delete) 8 | * [Timer](#timer) 9 | * [The time entry object](#object) 10 | * [Dependent objects](#dependencies) 11 | 12 | 13 | ## Getting time entries 14 | 15 | You can list timesheet entries by making a GET request to: 16 | 17 | * `/api/entries?where=project_id=[PROJECT_ID]` for a list of time entries from a project 18 | * `/api/entries?where=task_id=[TASK_ID]` for a list of time entries for a task 19 | * `/api/entries?where=user_id=[USER_ID]` for a list of time entries for a user 20 | * `/api/entries?where=client_id=[CLIENT_ID]` for a list of time entries for a client 21 | * `/api/entries?where=time_interval in ("2014-12-01T00:00:00Z","2015-01-01T00:00:00Z")` for a list of time entries between Dec 1st, 2014 and Jan 1st, 2015. 22 | 23 | Example of response: 24 | 25 | ```json 26 | { 27 | "entries": [ 28 | { 29 | "id": 15200864, 30 | "task_id": 241184, 31 | "user_id": 1563, 32 | "start_time": "2014-12-11T09:30:00Z", 33 | "end_time": "2014-12-11T10:15:00Z", 34 | "description": "", 35 | "added_manually": true, 36 | "invoice_item_id": null, 37 | "billed": false, 38 | "is_bulk": false, 39 | "project_id": 28934, 40 | "created_on": "2014-12-12T14:42:49Z", 41 | "updated_on": "2014-12-12T14:42:49Z", 42 | "duration": 2700 43 | }, 44 | { 45 | "id": 12186606, 46 | "task_id": 2093082, 47 | "user_id": 1563, 48 | "description": "", 49 | "added_manually": false, 50 | "invoice_item_id": null, 51 | "billed": false, 52 | "is_bulk": true, 53 | "project_id": 347452, 54 | "created_on": "2014-10-21T09:07:39Z", 55 | "updated_on": "2014-10-21T09:07:39Z", 56 | "duration": 21600, 57 | "date": "2014-10-21" 58 | } 59 | ] 60 | } 61 | ``` 62 | 63 | You can also [include related content](includes.md) when listing entries. 64 | 65 | 66 | ## Getting a time entry 67 | 68 | To get the time entry info, make a GET request to: 69 | 70 | * `/api/entries/[ENTRY_ID]` 71 | 72 | Example response: 73 | 74 | ```json 75 | { 76 | "entries": [ 77 | { 78 | "id": 15200864, 79 | "task_id": 241184, 80 | "user_id": 1563, 81 | "start_time": "2014-12-11T09:30:00Z", 82 | "end_time": "2014-12-11T10:15:00Z", 83 | "description": "", 84 | "added_manually": true, 85 | "invoice_item_id": null, 86 | "billed": false, 87 | "is_bulk": false, 88 | "project_id": 28934, 89 | "created_on": "2014-12-12T14:42:49Z", 90 | "updated_on": "2014-12-12T14:42:49Z", 91 | "duration": 2700 92 | } 93 | ] 94 | } 95 | ``` 96 | 97 | You can also [include related content](includes.md) when getting a time entry. 98 | 99 | 100 | ## Creating a time entry 101 | 102 | To create a time entry, make a POST request to: 103 | 104 | * `/api/entries` 105 | 106 | with the request body containing the new time entry info, as in the examples below: 107 | 108 | ```json 109 | { 110 | "task_id": 241184, 111 | "start_time": "2014-12-10T09:00:00Z", 112 | "end_time": "2014-12-10T10:00:00Z", 113 | "description": "Talked to Susan on the phone." 114 | } 115 | ``` 116 | 117 | to add a time entry with a start and end time, or: 118 | 119 | ```json 120 | { 121 | "task_id": 241184, 122 | "date": "2014-12-10", 123 | "duration": 3600, 124 | "description": "Talked to Peter on the phone." 125 | } 126 | ``` 127 | 128 | to add a time entry with a date and duration. Duration is the length of the entry in _seconds_. 129 | 130 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new time entry. The response body will contain the new time entry info as in the **Getting a time entry** section. 131 | 132 | ### Required fields 133 | 134 | When creating a time entry: `task_id`, (`date` and `duration`) or (`start_time` and `end_time`). 135 | 136 | ### Restrictions 137 | 138 | When creating a time entry with `start_time` and `end_time` the minimal time entry duration is 1 minute. 139 | 140 | 141 | ## Updating a time entry 142 | 143 | To update an existing time entry, make a POST or PUT request to: 144 | 145 | * `/api/entries/[ENTRY_ID]` 146 | 147 | with the request body containing the updated info. You can send only the changed fields. 148 | 149 | Example of request body if you want to change the duration: 150 | 151 | ```json 152 | { 153 | "duration": 7200 154 | } 155 | ``` 156 | 157 | You can change the `duration` only for the time entries that were added with `date` and `duration` parameters. 158 | 159 | For entries added with `start_time` and `end_time` the duration can be adjusted by updating the `end_time`. 160 | 161 | The response will return `200 OK` and will contain the updated time entry info as in the **Getting a time entry** section. 162 | 163 | ### Restrictions 164 | 165 | When updating an entry with `start_time` and `end_time` the new duration cannot be less than 1 minute. 166 | 167 | 168 | ## Deleting a time entry 169 | 170 | To delete a time entry, make a DELETE request to: 171 | 172 | * `/api/entries/[ENTRY_ID]` 173 | 174 | If successful, the response will have a `200 OK` status code. 175 | 176 | 177 | ## Timer 178 | 179 | A timer is a special kind of a time entry with the `end_time` not yet set. 180 | 181 | Any user can have at most only one timer started at any time. 182 | 183 | ### Starting a timer 184 | 185 | Starting the timer for a user is the same as creating a time entry for that user with `start_time` and no `end_time`. 186 | 187 | For example, make a POST request to: 188 | 189 | * `/api/entries` with the body: 190 | 191 | ```json 192 | { 193 | "task_id": 241184, 194 | "user_id": 1563, 195 | "description": "Running timer description", 196 | "start_time": "2017-06-20T09:30:00Z" 197 | } 198 | ``` 199 | 200 | If another timer is already running for that user, the request will fail with a 409 error code. 201 | 202 | ### Stopping a timer 203 | 204 | Stopping the timer is the same as updating the timer's `end_time` with a valid date and time value. 205 | 206 | For example, make a PUT request to: 207 | 208 | * `/api/entries/[TIMER_ID]` with the body: 209 | 210 | ```json 211 | { 212 | "end_time": "2017-06-20T15:00:00Z", 213 | "description": "Final time entry description" 214 | } 215 | ``` 216 | 217 | Same restrictions as for time entries apply for timers. You cannot stop a timer if the final duration is less than a minute. 218 | To stop the timer in this case, make a DELETE request and delete the timer. 219 | 220 | ### Listing running timers 221 | 222 | To get the running timer of any particular user, make a GET request to: 223 | 224 | * `/api/entries?where=user_id=[USER_ID] and end_time=null` 225 | 226 | 227 | ## The time entry object 228 | 229 | A time entry object has the following attributes: 230 | 231 | Attribute|Type|Description 232 | ---------|----|----------- 233 | id | integer | _(read-only)_ Unique entry identifier 234 | project_id | integer | _(read-only)_ Project id 235 | task_id | integer | Id of the task for which the time is added 236 | user_id | integer | Id of the user for whom the time is added 237 | is_bulk | boolean | If `true` the entry was added with `date` and `duration`, `false` if entry was added with `start_time` and `end_time`. 238 | start_time | datetime | Date and time when the time entry started. Note: `start_time` and `end_time` are not present for entries with `date` and `duration`. 239 | end_time | datetime | Date and time when the time entry ended 240 | date | date | Date when the time entry was added. Note: `date` and `duration` are not present for entries with `start_time` and `end_time`. 241 | duration | integer | Time entry duration in seconds 242 | description | text | Time entry description 243 | added_manually | boolean | If `false` the entry was added using a timer (using start/stop in Paymo Widget). 244 | billed | boolean | If `true` the entry is billed. Time for the entry was added into an invoice. 245 | invoice_item_id | integer | Id of the invoice item if the entry was billed (added into an invoice). 246 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the entry was created 247 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the entry was last updated 248 | 249 | 250 | ## Dependent objects 251 | 252 | The following object types can be used in [includes](includes.md): 253 | 254 | Object type|Include key|Relationship 255 | -----------|-----------|---- 256 | [Task](tasks.md) | task | parent 257 | [Invoice item](invoices.md) | invoiceitem | parent 258 | [User](users.md) | user | parent 259 | 260 | -------------------------------------------------------------------------------- /sections/project_statuses.md: -------------------------------------------------------------------------------- 1 | # Project statuses 2 | 3 | * [Listing project statuses](#list) 4 | * [Getting a project status](#get) 5 | * [Creating a project status](#create) 6 | * [Updating a project status](#update) 7 | * [Deleting a project status](#delete) 8 | * [The project status object](#object) 9 | * [Dependent objects](#dependencies) 10 | 11 | 12 | ## Listing project statuses 13 | 14 | You can list your project statuses by making a GET request to: 15 | 16 | * `/api/projectstatuses` for a list of all statuses 17 | * `/api/projectstatuses?include=projects` for a list of project statuses with the projects info also included 18 | 19 | Example of response: 20 | 21 | ```json 22 | { 23 | "projectstatuses": [ 24 | { 25 | "id": 28282, 26 | "name": "Active", 27 | "active": true, 28 | "readonly": true, 29 | "seq": 0, 30 | "created_on": "2017-07-12T09:02:39Z", 31 | "updated_on": "2017-07-12T09:02:39Z" 32 | }, 33 | { 34 | "id": 28283, 35 | "name": "Proposal", 36 | "active": true, 37 | "readonly": false, 38 | "seq": 1, 39 | "created_on": "2017-07-12T09:02:39Z", 40 | "updated_on": "2017-07-12T09:02:39Z" 41 | }, 42 | { 43 | "id": 28284, 44 | "name": "On-hold", 45 | "active": false, 46 | "readonly": false, 47 | "seq": 2, 48 | "created_on": "2017-07-12T09:02:39Z", 49 | "updated_on": "2017-07-12T09:02:39Z" 50 | }, 51 | { 52 | "id": 28285, 53 | "name": "Cancelled", 54 | "active": false, 55 | "readonly": false, 56 | "seq": 3, 57 | "created_on": "2017-07-12T09:02:39Z", 58 | "updated_on": "2017-07-12T09:02:39Z" 59 | }, 60 | { 61 | "id": 28286, 62 | "name": "Completed", 63 | "active": false, 64 | "readonly": false, 65 | "seq": 4, 66 | "created_on": "2017-07-12T09:02:39Z", 67 | "updated_on": "2017-07-12T09:02:39Z" 68 | }, 69 | { 70 | "id": 28287, 71 | "name": "Archived", 72 | "active": false, 73 | "readonly": true, 74 | "seq": 5, 75 | "created_on": "2017-07-12T09:02:39Z", 76 | "updated_on": "2017-07-12T09:02:39Z" 77 | } 78 | ] 79 | } 80 | ``` 81 | 82 | 83 | ## Getting a project status 84 | 85 | To get the project status info, make a GET request to: 86 | 87 | * `/api/projectstatuses/[STATUS_ID]` for basic status info 88 | * `/api/projectstatuses/[STATUS_ID]?include=projects` for a complete info about the status, including projects 89 | 90 | Example response: 91 | 92 | ```json 93 | { 94 | "projectstatuses": [ 95 | { 96 | "id": 28282, 97 | "name": "Active", 98 | "active": true, 99 | "readonly": true, 100 | "seq": 0, 101 | "created_on": "2017-07-12T09:02:39Z", 102 | "updated_on": "2017-07-12T09:02:39Z" 103 | } 104 | ] 105 | } 106 | ``` 107 | 108 | An example response with projects info included: 109 | 110 | ```json 111 | { 112 | "projectstatuses": [ 113 | { 114 | "id": 28282, 115 | "name": "Active", 116 | "active": true, 117 | "readonly": true, 118 | "seq": 0, 119 | "created_on": "2017-07-12T09:02:39Z", 120 | "updated_on": "2017-07-12T09:02:39Z", 121 | "projects": [ 122 | { 123 | "id": 1383832, 124 | "name": "Paymo Walkthrough", 125 | "code": "PW", 126 | "task_code_increment": 7, 127 | "description": "This walkthrough sample project contains a collection of tasks/to-dos that help describe the basics of using Paymo. If you need help you can always get in touch with us using the small chat icon in the bottom right corner of the screen.", 128 | "client_id": 923821, 129 | "status_id": 28282, 130 | "active": true, 131 | "adjustable_hours": null, 132 | "hourly_billing_mode": null, 133 | "budget_hours": 0.25, 134 | "price_per_hour": 60, 135 | "estimated_price": null, 136 | "price": null, 137 | "invoiced": false, 138 | "invoice_item_id": null, 139 | "billable": false, 140 | "flat_billing": false, 141 | "color": "#68BE5E", 142 | "users": [ 143 | 19393 144 | ], 145 | "managers": [ 146 | 19393 147 | ], 148 | "created_on": "2017-07-12T09:02:39Z", 149 | "updated_on": "2017-07-12T09:02:39Z", 150 | "billing_type": "non" 151 | }, 152 | { 153 | "id": 393932, 154 | "name": "Product downloader", 155 | "code": "PD", 156 | "task_code_increment": 54, 157 | "description": "", 158 | "client_id": 923821, 159 | "status_id": 28282, 160 | "active": true, 161 | "adjustable_hours": null, 162 | "hourly_billing_mode": null, 163 | "budget_hours": 105, 164 | "price_per_hour": 90, 165 | "estimated_price": 0, 166 | "price": null, 167 | "invoiced": false, 168 | "invoice_item_id": null, 169 | "billable": true, 170 | "flat_billing": false, 171 | "color": null, 172 | "users": [ 173 | 92983, 174 | 2392029 175 | ], 176 | "managers": [ 177 | 92983 178 | ], 179 | "created_on": "2017-07-12T09:02:39Z", 180 | "updated_on": "2017-07-12T09:06:37Z", 181 | "billing_type": "pph" 182 | } 183 | ] 184 | } 185 | ] 186 | } 187 | ``` 188 | 189 | 190 | ## Creating a project status 191 | 192 | To create a project status, make a POST request to: 193 | 194 | * `/api/projectstatuses` 195 | 196 | with the request body containing the new status info. 197 | 198 | Sample request body to create a status: 199 | 200 | ```json 201 | { 202 | "name": "Preparation" 203 | } 204 | ``` 205 | 206 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new project status. The response body will contain the new project status info as in the **Getting a project status** section. 207 | 208 | ### Required fields 209 | 210 | When creating a project status: `name`. 211 | For a complete description of all project status fields, see [project status object](#object). 212 | 213 | 214 | ## Updating a project status 215 | 216 | To update an existing project status, make a POST or PUT request to: 217 | 218 | * `/api/projectstatuses/[STATUS_ID]` 219 | 220 | with the request body containing the updated info. You can send only the changed fields. 221 | 222 | Example of request body if you want to change the status name: 223 | 224 | ```json 225 | { 226 | "name": "New status name" 227 | } 228 | ``` 229 | 230 | 231 | ## Deleting a project status 232 | 233 | To delete a project status, make a DELETE request to: 234 | 235 | * `/api/projectstatuses/[STATUS_ID]` 236 | 237 | If successful, the response will have a `200 OK` status code. 238 | 239 | 240 | ## The project status object 241 | 242 | A project status object has the following attributes: 243 | 244 | Attribute|Type|Description 245 | ---------|----|----------- 246 | id | integer | _(read-only)_ Unique status identifier 247 | name | text | Status name 248 | active | boolean | If true the project is being active (you can add time to its tasks), otherwise it is archived (you cannot add time to its tasks) 249 | seq | integer | Position (order) of the status 250 | readonly | boolean | _(read-only)_ If 'true' the status cannot be edited or deleted. 251 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the project status was created 252 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the project status was last updated 253 | 254 | 255 | 256 | ## Dependent objects 257 | The following object types can be used in [includes](includes.md): 258 | 259 | Object type|Include key|Relationship 260 | -----------|-----------|---- 261 | [Project](projects.md) | project | parent 262 | 263 | -------------------------------------------------------------------------------- /sections/files.md: -------------------------------------------------------------------------------- 1 | # Files 2 | 3 | * [Getting files](#list) 4 | * [Getting a file](#get) 5 | * [Uploading a file](#create) 6 | * [Updating a file](#update) 7 | * [Attaching an unattached file](#attach) 8 | * [Deleting a file](#delete) 9 | * [The file object](#object) 10 | * [Dependent objects](#dependencies) 11 | 12 | In Paymo files can be attached to projects. Besides this, inside a project, files can be attached to discussions, tasks or comments. 13 | 14 | 15 | ## Getting files 16 | 17 | You can list files by making a GET request to: 18 | 19 | * `/api/files?where=project_id=[PROJECT_ID]` for a list of all the files from a project 20 | * `/api/files?where=discussion_id=[DISCUSSION_ID]` for a list of files attached to a discussion 21 | * `/api/files?where=task_id=[TASK_ID]` for a list of files attached to a task 22 | * `/api/files?where=comment_id=[COMMENT_ID]` for a list of files attached to a comment 23 | 24 | Example of response: 25 | 26 | ```json 27 | { 28 | "files": [ 29 | { 30 | "id": 135371, 31 | "original_filename": "DSC00782.jpg", 32 | "user_id": 23129, 33 | "project_id": 397709, 34 | "comment_id": 125234, 35 | "size": "517429", 36 | "mime": "image/jpeg", 37 | "description": "", 38 | "created_on": "2015-01-26T14:52:15Z", 39 | "updated_on": "2015-01-26T14:52:17Z", 40 | "file": "https://app.paymoapp.com/assets/13980/projects/397709/41bba5de4f1ed25eb6ee626656df88fd.jpg", 41 | "image_thumb_large": "https://app.paymoapp.com/assets/13980/projects/397709/thumbs/large/41bba5de4f1ed25eb6ee626656df88fd.png", 42 | "image_thumb_medium": "https://app.paymoapp.com/assets/13980/projects/397709/thumbs/medium/41bba5de4f1ed25eb6ee626656df88fd.png", 43 | "image_thumb_small": "https://app.paymoapp.com/assets/13980/projects/397709/thumbs/small/41bba5de4f1ed25eb6ee626656df88fd.png", 44 | "tags": [] 45 | }, 46 | { 47 | "id": 135372, 48 | "original_filename": "Metamodel_Represented_in_UML_notation_v01.png", 49 | "user_id": 23129, 50 | "project_id": 397709, 51 | "task_id": 2322874, 52 | "size": "363491", 53 | "mime": "image/jpeg", 54 | "description": "", 55 | "created_on": "2015-01-26T15:05:52Z", 56 | "updated_on": "2015-01-26T15:05:54Z", 57 | "file": "https://app.paymoapp.com/assets/13980/projects/397709/d82674e90692d21cedcb31fbcb13f727.png", 58 | "image_thumb_large": "https://app.paymoapp.com/assets/13980/projects/397709/thumbs/large/d82674e90692d21cedcb31fbcb13f727.png", 59 | "image_thumb_medium": "https://app.paymoapp.com/assets/13980/projects/397709/thumbs/medium/d82674e90692d21cedcb31fbcb13f727.png", 60 | "image_thumb_small": "https://app.paymoapp.com/assets/13980/projects/397709/thumbs/small/d82674e90692d21cedcb31fbcb13f727.png", 61 | "tags": [] 62 | } 63 | ] 64 | } 65 | ``` 66 | 67 | You can also [include related content](includes.md) when listing files. 68 | 69 | 70 | ## Getting a file 71 | 72 | To get the file info, make a GET request to: 73 | 74 | * `/api/files/[FILE_ID]` 75 | 76 | Example response: 77 | 78 | ```json 79 | { 80 | "files": [ 81 | { 82 | "id": 135371, 83 | "original_filename": "DSC00782.jpg", 84 | "user_id": 23129, 85 | "project_id": 397709, 86 | "comment_id": 125234, 87 | "size": "517429", 88 | "mime": "image/jpeg", 89 | "description": "", 90 | "created_on": "2015-01-26T14:52:15Z", 91 | "updated_on": "2015-01-26T14:52:17Z", 92 | "file": "https://app.paymoapp.com/assets/13980/projects/397709/41bba5de4f1ed25eb6ee626656df88fd.jpg", 93 | "image_thumb_large": "https://app.paymoapp.com/assets/13980/projects/397709/thumbs/large/41bba5de4f1ed25eb6ee626656df88fd.png", 94 | "image_thumb_medium": "https://app.paymoapp.com/assets/13980/projects/397709/thumbs/medium/41bba5de4f1ed25eb6ee626656df88fd.png", 95 | "image_thumb_small": "https://app.paymoapp.com/assets/13980/projects/397709/thumbs/small/41bba5de4f1ed25eb6ee626656df88fd.png", 96 | "tags": [] 97 | } 98 | ] 99 | } 100 | ``` 101 | 102 | You can also [include related content](includes.md) when getting a file. 103 | 104 | 105 | ## Uploading a file 106 | 107 | To upload a file, make a POST request to: 108 | 109 | * `/api/files` 110 | 111 | The request content-type should be `multipart-form-data` and the file field name equal to `file`. To attach the file at the same time with the upload, send the `project_id` or `discussion_id` or `task_id` or `comment_id` data in the request. 112 | 113 | Here's an example using `curl` command line for uploading a file to a project: 114 | 115 | ```shell 116 | curl -u email:password 117 | -H 'Accept: application/json' 118 | -F "file=@file.jpg" 119 | -F "project_id=123456" 120 | https://app.paymoapp.com/api/files 121 | ``` 122 | 123 | or an example of uploading a file and attaching it to a task: 124 | 125 | ```shell 126 | curl -u email:password 127 | -H 'Accept: application/json' 128 | -F "file=@file.jpg" 129 | -F "task_id=90000" 130 | https://app.paymoapp.com/api/files 131 | ``` 132 | 133 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new file. The response body will contain the new file info. 134 | 135 | If you do not send a `project_id` or a `discussion_id` or a `task_id` or a `comment_id`, the file will not be attached to anything, but it will have a `token` attribute. 136 | Use this token to attach the file to a project, a discussion, a task or a comment. 137 | The file will remain in a temporary location until it is attached. 138 | 139 | If you have trouble getting your code to work for uploads, take a look at this [sample PHP script](samples/file_upload.php). 140 | 141 | ### Required fields 142 | 143 | When uploading a file: `file`. 144 | 145 | 146 | ## Updating a file 147 | 148 | To update an existing file, make a POST or PUT request to: 149 | 150 | * `/api/files/[FILE_ID]` 151 | 152 | with the request body containing the updated info. Only the following fields can be updated: `original_filename` (for renaming a file) and `description`. 153 | 154 | Example of request body: 155 | 156 | ```json 157 | { 158 | "description": "latest design changes" 159 | } 160 | ``` 161 | 162 | The response will return `200 OK` and will contain the updated file info as in the **Getting a file** section. 163 | 164 | 165 | ## Attaching an unattached file 166 | 167 | For a file added with no `project_id` info you get a `token` attribute. To attach this file to a project (or discussion, or task, or comment), make a POST or PUT request to: 168 | 169 | * `/api/files/[FILE_ID]` 170 | 171 | with the `token` and `project_id` (or `discussion_id` or `task_id` or `comment_id`) in the request. 172 | 173 | Example of request body if you want to attach the uploaded file to a comment: 174 | 175 | ```json 176 | { 177 | "token": "060bee5d4f7aaf94ddd8629518260dc9", 178 | "comment_id": 123456 179 | } 180 | ``` 181 | 182 | 183 | ## Deleting a file 184 | 185 | To delete a file, make a DELETE request to: 186 | 187 | * `/api/files/[FILE_ID]` 188 | 189 | If successful, the response will have a `200 OK` status code. 190 | 191 | 192 | ## The file object 193 | 194 | A file object has the following attributes: 195 | 196 | Attribute|Type|Description 197 | ---------|----|----------- 198 | id | integer | _(read-only)_ Unique file identifier 199 | original_filename | text | Original filename that was used when uploading. Note that it differs from the filenames in download links. 200 | description | text | File description 201 | user_id | integer | _(read-only)_ Id of the user who uploaded the file 202 | project_id | integer | _(read-only)_ Id of the project the file is attached to 203 | discussion_id | integer | _(read-only)_ Id of the discussion the file is attached to (Only one from the list exists: `discussion_id` or `task_id` or `comment_id`) 204 | task_id | integer | _(read-only)_ Id of the task the file is attached to 205 | comment_id | integer | _(read-only)_ Id of the comment the file is attached to 206 | token | text | _(read-only)_ Token for temporary files. Use this token to attach the file. 207 | size | integer | _(read-only)_ File size in bytes 208 | file | text | _(read-only)_ Download link for the file 209 | image_thumb_large | text | _(read-only)_ Large thumbnail URL for image files 210 | image_thumb_medium | text | _(read-only)_ Medium thumbnail URL for image files 211 | image_thumb_small | text | _(read-only)_ Small thumbnail URL for image files 212 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the file was created 213 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the file was last updated 214 | 215 | 216 | ## Dependent objects 217 | 218 | The following object types can be used in [includes](includes.md): 219 | 220 | Object type|Include key|Relationship 221 | -----------|-----------|---- 222 | [Project](projects.md) | project | parent 223 | [User](users.md) | user | parent 224 | [Discussion](discussions.md) | discussion | parent 225 | [Task](tasks.md) | task | parent 226 | [Comment](comments.md) | comment | parent 227 | 228 | -------------------------------------------------------------------------------- /sections/task_recurring_profiles.md: -------------------------------------------------------------------------------- 1 | # Task recurring profiles 2 | 3 | * [Getting task recurring profiles](#list) 4 | * [Getting a task recurring profile](#get) 5 | * [Creating a task recurring profile](#create) 6 | * [Updating a task recurring profile](#update) 7 | * [Deleting a task recurring profile](#delete) 8 | * [The task recurring profile object](#object) 9 | * [Dependent objects](#dependencies) 10 | 11 | 12 | ## Getting task recurring profiles 13 | 14 | You can list task recurring profiles by making a GET request to: 15 | 16 | * `/api/taskrecurringprofiles` for a list of all task recurring profile from all projects 17 | * `/api/taskrecurringprofiles?where=project_id=[PROJECT_ID]` for a list of task recurring profiles from a project 18 | 19 | Example of response: 20 | 21 | ```json 22 | { 23 | "taskrecurringprofiles": [ 24 | { 25 | "id": 1, 26 | "name": "Design", 27 | "project_id": 4, 28 | "tasklist_id": 21, 29 | "user_id": 1, 30 | "task_user_id": null, 31 | "company_id": 1, 32 | "billable": true, 33 | "flat_billing": false, 34 | "description": "", 35 | "price_per_hour": null, 36 | "estimated_price": null, 37 | "budget_hours": null, 38 | "users": [], 39 | "priority": 50, 40 | "notifications": "{}", 41 | "frequency": "daily", 42 | "interval": 1, 43 | "on_day": [], 44 | "occurrences": null, 45 | "until": "2020-04-25", 46 | "active": true, 47 | "due_date_offset": 0, 48 | "recurring_start_date": "2020-04-22", 49 | "generated_count": null, 50 | "last_generated_on": null, 51 | "next_processing_date": "2020-04-23", 52 | "processing_timezone": "US/Central", 53 | "processing_hour": "06:00:00", 54 | "created_on": "2020-04-22T08:21:34Z", 55 | "updated_on": "2020-04-22T08:21:34Z" 56 | },{ 57 | "id": 2, 58 | "...": "..." 59 | } 60 | ] 61 | } 62 | ``` 63 | 64 | You can also [include related content](includes.md) when listing task recurring profiles. 65 | 66 | 67 | ## Getting a task recurring profile 68 | 69 | To get the task recurring profile info, make a GET request to: 70 | 71 | * `/api/taskrecurringprofiles/[TAKSRECURRINGPROFILE_ID]` 72 | 73 | Example response: 74 | 75 | ```json 76 | { 77 | "taskrecurringprofiles": [ 78 | { 79 | "id": 2, 80 | "name": "Evaluation", 81 | "project_id": 9, 82 | "tasklist_id": 28, 83 | "user_id": 1, 84 | "task_user_id": null, 85 | "company_id": 1, 86 | "billable": true, 87 | "flat_billing": false, 88 | "description": "", 89 | "price_per_hour": null, 90 | "estimated_price": null, 91 | "budget_hours": 11, 92 | "users": [ 93 | 3, 94 | 8, 95 | 9 96 | ], 97 | "priority": 50, 98 | "notifications": "{\"0\":{\"notification_enabled\":\"1\",\"notification_type\":\"task_due_date_reminder\",\"param\":\"24\"}}", 99 | "frequency": "weekly", 100 | "interval": 1, 101 | "on_day": [ 102 | 1, 103 | 3 104 | ], 105 | "occurrences": 11, 106 | "until": null, 107 | "active": true, 108 | "due_date_offset": 3, 109 | "recurring_start_date": "2020-04-23", 110 | "generated_count": null, 111 | "last_generated_on": null, 112 | "next_processing_date": "2020-04-27", 113 | "processing_timezone": "US/Central", 114 | "processing_hour": "06:00:00", 115 | "created_on": "2020-04-22T08:26:25Z", 116 | "updated_on": "2020-04-22T09:31:05Z" 117 | } 118 | ] 119 | } 120 | ``` 121 | 122 | You can also [include related content](includes.md) when getting a task recurring profiles. 123 | 124 | 125 | ## Creating a task recurring profile 126 | 127 | To create a task recurring profile, make a POST request to: 128 | 129 | * `/api/taskrecurringprofiles` 130 | 131 | with the request body containing the new task recurring profile info, as in the example below: 132 | 133 | ```json 134 | { 135 | "id": 1, 136 | "name": "Design", 137 | "project_id": 4, 138 | "frequency": "weekly", 139 | "interval": "1", 140 | "recurring_start_date": "2020-04-12" 141 | } 142 | ``` 143 | 144 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new task recurring profile. The response body will contain the new task recurring profile info as in the **Getting a task recurring profile** section. 145 | 146 | ### Required fields 147 | 148 | When creating a task recurring profile: `name` and `project_id` or `task_id` and then `frequency`, `interval`, `recurring_start_date` 149 | 150 | Using a `task_id` will import all data from that task and disregard any other fields sent in request related to task 151 | but will keep all fields sent in request specific to recurrence (like `frequency`, `interval`, `on_day`, `occurrences`, `active`, `recurring_start_date`... ): 152 | { 153 | "id": 1, 154 | "name": "Design", 155 | "task_id": 10, 156 | "frequency": "weekly", 157 | "interval": "1", 158 | "recurring_start_date": "2020-04-12" 159 | } 160 | The above will take `name` from the task with id 10 and disregard "name": "Design". 161 | 162 | 163 | ## Updating a task recurring profile 164 | 165 | To update an existing task recurring profile, make a POST or PUT request to: 166 | 167 | * `/api/taskrecurringprofiles/[TASKRECURRINGPROFILE_ID]` 168 | 169 | with the request body containing the updated info. You can send only the changed fields. 170 | 171 | Example of request body if you want to change the task recurring profile name: 172 | 173 | ```json 174 | { 175 | "name": "Changes to logo" 176 | } 177 | ``` 178 | 179 | The response will return `200 OK` and will contain the updated task recurring profile info as in the **Getting a task recurring profile** section. 180 | 181 | 182 | ## Deleting a task recurring profile 183 | 184 | To delete a task recurring profile, make a DELETE request to: 185 | 186 | * `/api/taskrecurringprofiles/[TASKRECURRINGPROFILE_ID]` 187 | 188 | If successful, the response will have a `200 OK` status code. 189 | 190 | 191 | ## The task recurring profile object 192 | 193 | A task recurring profile object has the following attributes: 194 | 195 | Attribute|Type|Description 196 | ---------|----|----------- 197 | id | integer | _(read-only)_ Unique task identifier 198 | name | text | Task name 199 | code | text | | _(read-only)_ | Task code format -. 200 | project_id | integer | Project id 201 | tasklist_id | integer | Task list id 202 | user_id | integer | _(read-only)_ Id of the user who created the task recurring profile 203 | task_user_id | integer | _(read-only)_ Id of the user who created the task if task recurring profile is created based on a task. 204 | billable | boolean | Only for tasks from billable projects, if `true` the tasks created will be billable. See [billing](#billing). 205 | flat_billing | boolean | Only for tasks from time & materials projects, if `true` the tasks created will have a flat rate. See [billing](#billing). 206 | description | text | Task description and notes 207 | price_per_hour | decimal | For billable time based tasks, price per hour when billing the time for tasks created with this profile 208 | estimated_price | decimal | The estimated task price 209 | budget_hours | decimal | Budget hours for the task 210 | users | list | List of user ids that are assigned to the task 211 | priority | integer | Task priority. See [priority](#priority) 212 | notifications | text | An array of notifications where each item must have `notification_type` and `param` representing alert threshold value Ex: {"notification_enabled":"1","notification_type":"task_due_date_reminder","param":"24"} 213 | frequency | text | Frequency can only be `daily`, `weekly` and `monthly` 214 | interval | integer | Represents after how many frequency periods to create task 215 | on_day | text | The particular month day to create task 216 | occurrences | integer | After how many occurrences to stop creating tasks 217 | until | date | Date until to keep creating tasks 218 | active | boolean | If task recurring profile is active or halted 219 | due_date_offset | integer | This is used as task duration 220 | recurring_start_date | date | This date will be considered as a reference for calculating the next date for generating a task. 221 | generated_count | integer | Number of generated tasks so far 222 | last_generated_on | date | Last date when a task ws generated 223 | next_processing_date | date | Next date when a task will be generated 224 | processing_timezone | text | Timezone used when generating tasks 225 | processing_hour | time | Hour used when generating tasks 226 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the task was created 227 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the task was last updated 228 | 229 | 230 | ## Dependent objects 231 | 232 | The following object types can be used in [includes](includes.md): 233 | 234 | Object type|Include key|Relationship 235 | -----------|-----------|---- 236 | [Project](projects.md) | project | parent 237 | -------------------------------------------------------------------------------- /sections/includes.md: -------------------------------------------------------------------------------- 1 | # Including related content 2 | 3 | Let's say you know the task ID and you want to get the task info together with the project name (in which the task is) and client name (for whom the project was created). 4 | 5 | Usually, what you do is: 6 | 7 | * Get the complete task info. That will include the project id. 8 | * Get the project info. That will get you the desired project name and the client id. 9 | * Get the client info, in which you finally get the client name, the last piece of the puzzle. 10 | 11 | In Paymo API you can get all that data in a single API request by using `include` param to the request. 12 | 13 | Example: 14 | 15 | ```shell 16 | curl -u email:password 17 | -H 'Accept: application/json' 18 | https://app.paymoapp.com/api/tasks/241147?include=project.name,project.client.name 19 | ``` 20 | 21 | And the response looks like: 22 | 23 | ```json 24 | { 25 | "tasks": [ 26 | { 27 | "id": 241147, 28 | "name": "Logo", 29 | "code": "PW-32", 30 | "project_id": 28917, 31 | "tasklist_id": 59917, 32 | "user_id": null, 33 | "complete": false, 34 | "billable": true, 35 | "seq": 1, 36 | "description": "", 37 | "price_per_hour": null, 38 | "due_date": "2013-09-25", 39 | "budget_hours": 80, 40 | "users": [ 41 | 1563 42 | ], 43 | "created_on": "2013-06-26T12:07:44Z", 44 | "updated_on": "2014-07-23T14:22:05Z", 45 | "project": { 46 | "name": "Best Buy Website - small changes", 47 | "client": { 48 | "name": "Best Buy Co., Inc." 49 | } 50 | } 51 | } 52 | ] 53 | } 54 | ``` 55 | 56 | You can include the entire related object info: 57 | 58 | ```shell 59 | curl -u email:password 60 | -H 'Accept: application/json' 61 | https://app.paymoapp.com/api/tasks/241147?include=project 62 | ``` 63 | 64 | The response looks like: 65 | 66 | ```json 67 | { 68 | "tasks": [ 69 | { 70 | "id": 241147, 71 | "name": "Logo", 72 | "code": "PW-32", 73 | "project_id": 28917, 74 | "tasklist_id": 59917, 75 | "user_id": null, 76 | "complete": false, 77 | "billable": true, 78 | "seq": 1, 79 | "description": "", 80 | "price_per_hour": null, 81 | "due_date": "2013-09-25", 82 | "budget_hours": 80, 83 | "users": [ 84 | 1563 85 | ], 86 | "created_on": "2013-06-26T12:07:44Z", 87 | "updated_on": "2014-07-23T14:22:05Z", 88 | "project": { 89 | "id": 28917, 90 | "name": "Best Buy Website - small changes", 91 | "description": "", 92 | "client_id": 10875, 93 | "active": false, 94 | "budget_hours": 700, 95 | "price_per_hour": 90, 96 | "billable": false, 97 | "color": null, 98 | "users": [ 99 | 1564, 100 | 1647 101 | ], 102 | "managers": [ 103 | 1647 104 | ], 105 | "created_on": "2013-06-26T12:07:44Z", 106 | "updated_on": "2015-01-09T13:40:15Z" 107 | } 108 | } 109 | ] 110 | } 111 | ``` 112 | 113 | Say you have the project ID and you want to list all task lists from that project and all tasks from those task lists. 114 | You can do it with the following request: 115 | 116 | ```shell 117 | curl -u email:password 118 | -H 'Accept: application/json' 119 | https://app.paymoapp.com/api/projects/28917?include=tasklists,tasklists.tasks 120 | ``` 121 | 122 | The response will look like: 123 | 124 | ```json 125 | { 126 | "projects": [ 127 | { 128 | "id": 28917, 129 | "name": "Best Buy Website - small changes", 130 | "code": "BBW", 131 | "task_code_increment": 19, 132 | "description": "", 133 | "client_id": 10875, 134 | "status_id": 39384, 135 | "active": false, 136 | "budget_hours": 700, 137 | "price_per_hour": 90, 138 | "billable": false, 139 | "color": null, 140 | "users": [ 141 | 1564, 142 | 1647 143 | ], 144 | "managers": [ 145 | 1647 146 | ], 147 | "created_on": "2013-06-26T12:07:44Z", 148 | "updated_on": "2015-01-09T13:40:15Z", 149 | "tasklists": [ 150 | { 151 | "id": 59917, 152 | "name": "Design", 153 | "project_id": 28917, 154 | "seq": 2, 155 | "milestone_id": 7939, 156 | "created_on": "2013-06-26T12:07:44Z", 157 | "updated_on": "2014-07-23T14:22:05Z", 158 | "tasks": [ 159 | { 160 | "id": 241147, 161 | "name": "Logo", 162 | "code": "PW-23", 163 | "project_id": 28917, 164 | "tasklist_id": 59917, 165 | "user_id": null, 166 | "complete": false, 167 | "billable": true, 168 | "seq": 1, 169 | "description": "", 170 | "price_per_hour": null, 171 | "due_date": "2013-09-25", 172 | "budget_hours": 80, 173 | "users": [ 174 | 1563 175 | ], 176 | "created_on": "2013-06-26T12:07:44Z", 177 | "updated_on": "2014-07-23T14:22:05Z" 178 | }, 179 | { 180 | "id": 266875, 181 | "name": "Header", 182 | "code": "PW-22", 183 | "project_id": 28917, 184 | "tasklist_id": 59917, 185 | "user_id": 1562, 186 | "complete": false, 187 | "billable": true, 188 | "seq": 2, 189 | "description": "", 190 | "price_per_hour": null, 191 | "due_date": "2013-09-18", 192 | "budget_hours": 35, 193 | "users": [ 194 | 1563 195 | ], 196 | "created_on": "2013-07-25T11:18:54Z", 197 | "updated_on": "2014-07-23T14:22:05Z" 198 | }, 199 | { 200 | "id": 266876, 201 | "name": "Mobile version", 202 | "code": "PW-21", 203 | "project_id": 28917, 204 | "tasklist_id": 59917, 205 | "user_id": 1562, 206 | "complete": false, 207 | "billable": true, 208 | "seq": 3, 209 | "description": "", 210 | "price_per_hour": null, 211 | "due_date": "2013-10-25", 212 | "budget_hours": 100, 213 | "users": [ 214 | 1569 215 | ], 216 | "created_on": "2013-07-25T11:19:07Z", 217 | "updated_on": "2014-07-23T14:22:05Z" 218 | } 219 | ] 220 | }, 221 | { 222 | "id": 59918, 223 | "name": "Coding", 224 | "project_id": 28917, 225 | "seq": 3, 226 | "milestone_id": null, 227 | "created_on": "2013-06-26T12:07:44Z", 228 | "updated_on": "2014-07-23T14:22:05Z", 229 | "tasks": [ 230 | { 231 | "id": 241148, 232 | "name": "HTML coding", 233 | "code": "RDS-2", 234 | "project_id": 28917, 235 | "tasklist_id": 59918, 236 | "user_id": null, 237 | "complete": false, 238 | "billable": true, 239 | "seq": 1, 240 | "description": "", 241 | "price_per_hour": null, 242 | "due_date": "2013-09-11", 243 | "budget_hours": 60, 244 | "users": [ 245 | 1570 246 | ], 247 | "created_on": "2013-06-26T12:07:44Z", 248 | "updated_on": "2014-07-23T14:22:05Z" 249 | }, 250 | { 251 | "id": 266874, 252 | "name": "Testing", 253 | "code": "RDS-3", 254 | "project_id": 28917, 255 | "tasklist_id": 59918, 256 | "user_id": 1562, 257 | "complete": false, 258 | "billable": true, 259 | "seq": 2, 260 | "description": "", 261 | "price_per_hour": null, 262 | "due_date": "2013-10-30", 263 | "budget_hours": null, 264 | "users": [ 265 | 1564 266 | ], 267 | "created_on": "2013-07-25T11:18:11Z", 268 | "updated_on": "2014-07-23T14:22:05Z" 269 | } 270 | ] 271 | } 272 | ] 273 | } 274 | ] 275 | } 276 | ``` 277 | 278 | -------------------------------------------------------------------------------- /sections/webhooks.md: -------------------------------------------------------------------------------- 1 | # Webhooks 2 | 3 | * [Events](#events) 4 | * [Listing webhooks](#list) 5 | * [Getting a webhook](#get) 6 | * [Creating a webhook](#create) 7 | * [Wildcard events](#wildcard) 8 | * [Webhook signatures](#signature) 9 | * [Updating a webhook](#update) 10 | * [Deleting a webhook](#delete) 11 | * [Notifications](#notifications) 12 | * [Webhook headers](#headers) 13 | * [Additional includes](#includes) 14 | 15 | Webhooks allow for 3rd parties to be notified when an event in Paymo occurs. 16 | 17 | 18 | At the moment, the following events can be hooked: 19 | 20 | - model.insert.Client 21 | - model.update.Client 22 | - model.delete.Client 23 | - model.insert.ClientContact 24 | - model.update.ClientContact 25 | - model.delete.ClientContact 26 | - model.insert.Project 27 | - model.update.Project 28 | - model.delete.Project 29 | - model.insert.Tasklist 30 | - model.update.Tasklist 31 | - model.delete.Tasklist 32 | - model.insert.Task 33 | - model.update.Task 34 | - model.delete.Task 35 | - model.insert.Invoice 36 | - model.update.Invoice 37 | - model.delete.Invoice 38 | - model.insert.InvoicePayment 39 | - model.update.InvoicePayment 40 | - model.delete.InvoicePayment 41 | - model.insert.Entry 42 | - model.update.Entry 43 | - model.delete.Entry 44 | - model.start.Entry 45 | - model.stop.Entry 46 | - model.insert.Milestone 47 | - model.update.Milestone 48 | - model.delete.Milestone 49 | - model.insert.Report 50 | - model.update.Report 51 | - model.delete.Report 52 | - model.insert.Expense 53 | - model.update.Expense 54 | - model.delete.Expense 55 | - model.insert.Estimate 56 | - model.update.Estimate 57 | - model.delete.Estimate 58 | - model.insert.Comment 59 | - model.update.Comment 60 | - model.delete.Comment 61 | - model.insert.User 62 | - model.update.User 63 | - model.delete.User 64 | - model.insert.Booking 65 | - model.update.Booking 66 | - model.delete.Booking 67 | 68 | 69 | ## Listing webhooks 70 | 71 | You can list your own webhooks by making a GET request to: 72 | 73 | * `/api/hooks` 74 | 75 | Example of response: 76 | 77 | ```json 78 | { 79 | "hooks": [ 80 | { 81 | "id": 1, 82 | "target_url": "https://myapp.com/paymo-insert-task-hook", 83 | "last_status_code": null, 84 | "event": "model.insert.Task", 85 | "where": null, 86 | "created_on": "2017-01-04T13:39:40Z", 87 | "updated_on": "2017-01-04T13:39:40Z" 88 | }, 89 | { 90 | "id": 2, 91 | "target_url": "https://myapp.com/paymo-delete-task-hook", 92 | "last_status_code": null, 93 | "event": "model.delete.Task", 94 | "where": "project_id=123456", 95 | "created_on": "2017-01-04T13:40:11Z", 96 | "updated_on": "2017-01-04T13:40:11Z" 97 | } 98 | ] 99 | } 100 | ``` 101 | 102 | 103 | 104 | ## Getting a webhook 105 | 106 | To get the webhook info, make a GET request to: 107 | 108 | * `/api/hooks/[WEBHOOK_ID]` 109 | 110 | Example response: 111 | 112 | ```json 113 | { 114 | "hooks": [ 115 | { 116 | "id": 1, 117 | "target_url": "https://myapp.com/paymo-insert-task-hook", 118 | "last_status_code": 200, 119 | "event": "model.insert.Task", 120 | "where": null, 121 | "created_on": "2017-01-04T13:39:40Z", 122 | "updated_on": "2017-01-04T13:39:40Z" 123 | } 124 | ] 125 | } 126 | ``` 127 | 128 | 129 | ## Creating a webhook 130 | 131 | To create a webhook, make a POST request to: 132 | 133 | * `/api/hooks` 134 | 135 | with the request body containing the new webhook info as in the example below: 136 | 137 | ```json 138 | { 139 | "target_url": "https://myapp.com/paymo/notifications", 140 | "event": "model.insert.Task" 141 | } 142 | ``` 143 | 144 | If successful, the response will contain the new webhook info. 145 | 146 | By providing the `where` param you can trigger webhooks only if the model matches the conditions from `where`. 147 | The `where` param has the same syntax as when used in `where` params of `GET` requests. See [response filtering](filtering.md). 148 | 149 | For example, you want to be notified only for new task event in a specific project. The create request for this webhook will look like: 150 | 151 | ```json 152 | { 153 | "target_url": "https://myapp.com/paymo/project-123-new-tasks", 154 | "event": "model.insert.Task", 155 | "where": "project_id=123" 156 | } 157 | ``` 158 | 159 | 160 | ## Wildcard events 161 | 162 | When creating a webhook, you can also use a wildcard for the webhook event using the `*` symbol. 163 | 164 | For example, a webhook created with: 165 | 166 | * `event`=`*` will be triggered every time any event from the [list](#events) occurs. 167 | * `event`=`model.insert.*` will be triggered every time an insert event occurs. 168 | * `event`=`*.Task` will be triggered every time an insert/update/delete event for a task occurs. 169 | 170 | When using wildcard events, you can distinguish between event types by using the 171 | `X-Paymo-Event` header to get the actual event that triggered the webhook. (See [headers](#headers)) 172 | 173 | *Notice:* When using `where` with wildcard events, the validity of the `where` param will not be 174 | checked when creating the webhook. It will be checked against an actual event when the event will occur. 175 | In case the `where` param could not be parsed the webhook will not be triggered. 176 | 177 | 178 | ## Webhook signatures 179 | 180 | When creating a webhook you can also provide a `secret` param. 181 | In this case Paymo will sign this webhook requests so you can verify that they originated from Paymo. 182 | This `secret` value will not be returned when listing webhooks or getting webhook details. 183 | 184 | These webhook triggers will contain a HTTP header `X-Paymo-Signature` which is the 185 | HMAC hex digest of the response body generated using the `sha1` hash function and the `secret` as the HMAC key. 186 | 187 | For example, a delete project webhook created with `secret`="secret" will have the header: 188 | 189 | ``` 190 | X-Paymo-Signature: sha1=dc03736e396e70138bf7af4ffaa2948cde42dcf1 191 | ``` 192 | 193 | for the body: 194 | 195 | ``` 196 | {"id":"1679584"} 197 | ``` 198 | 199 | 200 | ## Updating a webhook 201 | 202 | To update an existing webhook, make a POST or PUT request to: 203 | 204 | * `/api/hooks/[WEBHOOK_ID]` 205 | 206 | with the request body containing the updated info. You can send only the changed fields. 207 | 208 | On update the webhook's `last_response_code` will be reset. 209 | 210 | Example of request body if you want to change the webhook `target_url`: 211 | 212 | ```json 213 | { 214 | "target_url": "https://myotherapp.com/notifications" 215 | } 216 | ``` 217 | 218 | The response will return `200 OK` and will contain the updated webhook info. 219 | 220 | 221 | ## Deleting a webook 222 | 223 | By deleting a webhook, you will stop receiving notifications for the events specified in the webhook. 224 | 225 | To delete a webhook, send a DELETE request to: 226 | 227 | * `/api/hooks/[WEBHOOK_ID]` 228 | 229 | If successful, the response will have a `200 OK` status code. 230 | 231 | A webhook is also deleted when `target url` responds with a status code of `410 Gone`. 232 | 233 | 234 | ## Notifications 235 | 236 | When an [event](#events) occurs, Paymo will notify all webhooks that match the following criteria: 237 | 238 | - webhook event matches occured event 239 | - webhook was created by a user that has `access` rights to the object from the event 240 | 241 | Paymo makes a webhook notification by making a POST request to the webhook URL, where the request 242 | 243 | - has header `Content-type: application/json` 244 | - has body equal to a JSON representation of the object from the event. 245 | 246 | For example, if the hook was created with: 247 | 248 | ```json 249 | { 250 | "event": "model.insert.Task", 251 | "target_url": "https://app.com/notifications" 252 | } 253 | ``` 254 | 255 | when a task is created, and the user that created the hook has the rights to view the task, Paymo will make a POST request to `https://app.com/notifications` with a body similar to: 256 | 257 | ```json 258 | { 259 | "id":109403, 260 | "name":"New Task", 261 | "project_id":59032, 262 | "tasklist_id":250019, 263 | "user_id":1093, 264 | "complete":false, 265 | "billable":true, 266 | "flat_billing":false, 267 | "seq":1, 268 | "description":"", 269 | "price_per_hour":null, 270 | "estimated_price":null, 271 | "price":null, 272 | "invoiced":false, 273 | "invoice_item_id":null, 274 | "due_date":null, 275 | "start_date":null, 276 | "budget_hours":null, 277 | "users":[ 278 | 1093 279 | ], 280 | "created_on":"2016-09-29T09:46:55Z", 281 | "updated_on":"2016-09-29T09:46:55Z", 282 | "files_count":0, 283 | "comments_count":0, 284 | "project":{ 285 | "name":"AT&T Flyer Design" 286 | }, 287 | "tasklist":{ 288 | "name":"Planning" 289 | } 290 | } 291 | ``` 292 | 293 | For `insert` and `update` events, the JSON object representation is mostly the same as the response you get by making a GET request to `/api/tasks/[TASK_ID]`, except 294 | 295 | - There is no `tasks` parent node in the response. The task object is at the root of JSON. 296 | - There are additional attributes like `project` and `tasklist`, as in the GET request to `/api/tasks/[TASK_ID]?include=project.name,tasklist.name` 297 | 298 | For `delete` events, the notification content is a JSON object with a single attribute: the ID of the object that was deleted. For example: 299 | 300 | ```json 301 | { 302 | "id": 109404 303 | } 304 | ``` 305 | 306 | 307 | ## Webhook Headers 308 | Any request by Paymo to a target URL triggered by a webhook will have additional headers: 309 | 310 | * `X-Paymo-Webhook` with the ID of the webhook which is triggered 311 | * `X-Paymo-Event` with the event that was triggered (e.g. `model.insert.Task`) 312 | * `X-Paymo-Signature` with the HMAC hash of the request body when the webhook was created with a `secret` 313 | 314 | 315 | ## Additional includes in webhook notification body 316 | 317 | Object type|Equivalent request 318 | -----------|----------- 319 | [Client](clients.md#get) | `/clients/[CLIENT_ID]` 320 | [Project](projects.md#get) | `/projects/[PROJECT_ID]?include=client.name` 321 | [Task List](tasklists.md#get) | `/tasklists/[TASKLIST_ID]?include=project.name` 322 | [Task](tasks.md#get) | `/tasks/[TASK_ID]?include=*,progress_status,project.name,tasklist.name` 323 | [Invoice](invoices.md#get) | `/invoices/[INVOICE_ID]?include=invoiceitems,client.name` 324 | [Time Entry](entries.md#get) | `/entries/[ENTRY_ID]?include=task.name,user.name` 325 | [Milestone](milestones.md#get) | `/milestones/[MILESTONE_ID]?include=project.name` 326 | [Expense](expenses.md#get) | `/expenses/[EXPENSE_ID]?include=client.name,project.name` 327 | [Estimate](estimates.md#get) | `/estimates/[ESTIMATE_ID]?include=estimateitems,client.name` 328 | [Comment](comments.md#get) | `/comments/[COMMENT_ID]` 329 | 330 | 331 | -------------------------------------------------------------------------------- /sections/users.md: -------------------------------------------------------------------------------- 1 | # Users 2 | 3 | * [Getting users](#list) 4 | * [Getting a user](#get) 5 | * [Creating a user](#create) 6 | * [Updating a user](#update) 7 | * [Archiving or activating a user](#archive) 8 | * [Adding a profile photo](#add-image) 9 | * [Deleting a user](#delete) 10 | * [The user object](#object) 11 | * [Dependent objects](#dependencies) 12 | 13 | Paymo users are assigned to a company. The types of users are: 14 | 15 | * _Admins_, have rights to everything in a company. 16 | * _Employees_, have rights only to the projects they are assigned to. 17 | * _Project Managers_, same as _Employees_ users, but with manager roles for some projects. 18 | * _Guests_, limited access users for specific projects, usually represents a client. 19 | 20 | To access Paymo, a user must be activated. The opposite of being activated is being retired (or archived) from Paymo. 21 | Retired users cannot log into Paymo. 22 | 23 | The maximal number of active users in a company is set up in the Paymo subscription. Guest users do not count towards subscription user limit. 24 | 25 | 26 | ## Getting users 27 | 28 | You can list users by making a GET request to: 29 | 30 | * `/api/users` for a list of all users in the company. 31 | * `/api/users?where=active=true` for a list of active users only. 32 | * `/api/users?where=active=false` for a list of retired users. 33 | * `/api/users?where=type=Admin` for a list of admin users. 34 | * `/api/users?where=type=Employee` for a list of employees. 35 | 36 | Example response for listing requests: 37 | 38 | ```json 39 | { 40 | "users": [ 41 | { 42 | "id": 1, 43 | "name": "Michael Scott", 44 | "email": "michael@dundermifflin.com", 45 | "type": "Admin", 46 | "active": true, 47 | "phone": "860-437-7283", 48 | "skype": null, 49 | "position": "CEO", 50 | "workday_hours": 10, 51 | "price_per_hour": 80, 52 | "image": "https://app.paymoapp.com/assets/1/users/michael.jpg", 53 | "created_on": "2013-08-01T13:09:16Z", 54 | "updated_on": "2015-01-06T19:53:51Z", 55 | "timezone": "US/Eastern", 56 | "date_format": "m/d/Y", 57 | "decimal_sep": ".", 58 | "language": "en", 59 | "thousands_sep": "", 60 | "time_format": "H:i", 61 | "week_start": "1", 62 | "assigned_projects": [ 63 | 28917, 64 | ], 65 | "managed_projects": [ 66 | 28917 67 | ] 68 | }, 69 | { 70 | "id": 2, 71 | "name": "Dwight Schrute", 72 | "email": "dwight@dundermifflin.com", 73 | "type": "Employee", 74 | "active": true, 75 | "timezone": "US/Central", 76 | "phone": "860-437-1329", 77 | "skype": null, 78 | "position": "Marketing", 79 | "workday_hours": 8, 80 | "price_per_hour": 45, 81 | "image": "https://app.paymoapp.com/assets/1/users/dwight.jpg", 82 | "created_on": "2013-06-26T12:07:44Z", 83 | "updated_on": "2014-10-15T12:56:43Z", 84 | "date_format": "m/d/Y", 85 | "decimal_sep": ".", 86 | "language": "en", 87 | "time_format": "H:i", 88 | "week_start": "1", 89 | "assigned_projects": [ 90 | 28918, 91 | 28936 92 | ], 93 | "managed_projects": [ 94 | 28936 95 | ] 96 | } 97 | ] 98 | } 99 | ``` 100 | 101 | You can also [include related content](includes.md) when getting the list of users. 102 | 103 | 104 | ## Getting a user 105 | 106 | To get the user's info, make a GET request to: 107 | 108 | * `/api/users/[USER_ID]` 109 | 110 | Example of response: 111 | 112 | ```json 113 | { 114 | "users": [ 115 | { 116 | "id": 1, 117 | "name": "Michael Scott", 118 | "email": "michael@dundermifflin.com", 119 | "type": "Admin", 120 | "active": true, 121 | "phone": "860-437-7283", 122 | "skype": null, 123 | "position": "CEO", 124 | "workday_hours": 10, 125 | "price_per_hour": 80, 126 | "image": "https://app.paymoapp.com/assets/1/users/michael.jpg", 127 | "created_on": "2013-08-01T13:09:16Z", 128 | "updated_on": "2015-01-06T19:53:51Z", 129 | "timezone": "US/Eastern", 130 | "date_format": "m/d/Y", 131 | "decimal_sep": ".", 132 | "language": "en", 133 | "thousands_sep": "", 134 | "time_format": "H:i", 135 | "week_start": "1", 136 | "assigned_projects": [ 137 | 28917, 138 | ], 139 | "managed_projects": [ 140 | 28917 141 | ] 142 | } 143 | ] 144 | } 145 | ``` 146 | 147 | You can also [include related content](includes.md) when getting a user. 148 | 149 | 150 | ## Creating a user 151 | 152 | To create a user, make a POST request to: 153 | 154 | * `/api/users` 155 | 156 | with the request body containing the new user info, as in the example below: 157 | 158 | ```json 159 | { 160 | "email": "kelly@dundermifflin.com", 161 | "type": "Employee", 162 | "assigned_projects": [28917], 163 | "password": "secret" 164 | } 165 | ``` 166 | 167 | If successful, the response will return `201 Created`. The response body will contain the new user info as in the **Getting a user** section. 168 | 169 | If your company does not have a paid Paymo subscription or you have reached the active users limit set up by the subscription, you will get a `403 Error: Could not add user. Maximum number of users reached.` 170 | 171 | If the `password` attribute is sent, the user will have the desired password. If not, the user will not have a password set. 172 | If the user is added from the Paymo application, a Welcome to Paymo email will be sent which contains a link that will take the user through the setup process, where a name and a password will be set. 173 | 174 | ### Required fields 175 | 176 | When creating a user: `email`. 177 | 178 | 179 | ## Updating a user 180 | 181 | To update an existing user, make a POST or PUT request to: 182 | 183 | * `/api/users/[USER_ID]` 184 | 185 | with the request body containing the updated info. You can send only the changed fields. 186 | 187 | Example of request body if you want to change the list of assigned projects of a user: 188 | 189 | ```json 190 | { 191 | "assigned_projects": [28917, 28918] 192 | } 193 | ``` 194 | 195 | 196 | ## Changing user password 197 | 198 | To change a user's password, make an update request with new password, as in: 199 | 200 | ```json 201 | { 202 | "password": "new password" 203 | } 204 | ``` 205 | 206 | 207 | ## Archiving (retiring) or activating a user 208 | 209 | To archive a user, make an update request with the following request body: 210 | 211 | ```json 212 | { 213 | "active": false 214 | } 215 | ``` 216 | 217 | To activate, send a `true` value. 218 | 219 | 220 | ## Adding a profile photo 221 | 222 | To add a user profile photo, make a POST request to: 223 | 224 | * `/api/users/[USER_ID]` 225 | 226 | The request content-type should be `multipart-form-data` and the file field name equal to `image`. Here's an example using `curl` command line: 227 | 228 | ```shell 229 | curl -u email:password 230 | -H 'Accept: application/json' 231 | -F "image=@file.jpg" 232 | https://app.paymoapp.com/api/users/12345 233 | ``` 234 | 235 | Accepted image file formats are: JPEG, PNG, GIF. 236 | 237 | The profile image of an user can be added when creating the user. In that case, all the user fields should be send in `multipart-form-data` format together with the file. 238 | 239 | 240 | ## Deleting a user 241 | 242 | To delete a user, make a DELETE request to: 243 | 244 | * `/api/users/[USER_ID]` 245 | 246 | ### Warning 247 | 248 | **Deleting a user will also delete all time logged by that user!** 249 | 250 | 251 | ## The user object 252 | 253 | A user object has the following attributes: 254 | 255 | Attribute|Type|Description 256 | ---------|----|----------- 257 | id | integer | _(read-only)_ Unique user identifier 258 | name | text | Full name 259 | email | email | Email address. There are no two active users in Paymo with the same email address. Email is used to receive notifications from Paymo as well as to log into Paymo. 260 | type | text | Account type. Available options: `Admin`, `Employee`, `Guest`. 261 | active | boolean | If `true` the user is active and can use Paymo, otherwise it is retired (archived). 262 | timezone | text | User timezone. [List of available options](http://en.wikipedia.org/wiki/List_of_tz_database_time_zones) 263 | phone | text | Phone number 264 | skype | text | Skype account name 265 | position | text | Job position description 266 | workday_hours | decimal | Number of working hours in a day. It is used to compute user performance. 267 | price_per_hour | decimal | Price per hour. It is used in invoicing to compute the cost of worked time. 268 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the user was created 269 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the user was last updated 270 | image | url | User profile image URL 271 | image_thumb_large | url | _(read-only)_ User profile image large size thumbnail URL 272 | image_thumb_medium | url | _(read-only)_ User profile image medium size thumbnail URL 273 | image_thumb_small | url | _(read-only)_ User profile image small size thumbnail URL 274 | date_format | text | Format for displaying dates in the application. Available options: `Y-m-d`, `d/m/Y`, `m/d/Y`, `d.m.Y`. 275 | time_format | text | Format for displaying time values. Available options: `H:i` for 24-hour format, `h:i a` for 12-hour format. 276 | decimal_sep | text | Decimal separator for displaying numeric values 277 | thousands_sep | text | Thousands separator for displaying numeric values 278 | week_start | text | Numeric value in the range 0-6 representing the day the week starts, 0 being Sunday, 6 being Saturday. 279 | language | text | Paymo user interface language 280 | theme | text | Paymo user interface theme name 281 | assigned_projects | list | List of projects ids to which the user is assigned 282 | managed_projects | list | List of projects ids that the user manages. This list is a subset of `assigned_projects`. 283 | is_online | boolean | _(read-only)_ If `true` the user is logged into Paymo. 284 | password | text | _(only for create/update requests)_ User password when creating or updating a user. 285 | 286 | 287 | ## Dependent objects 288 | 289 | The following object types ca be used in [includes](includes.md): 290 | 291 | Object type|Include key 292 | -----------|----------- 293 | [Comment](comments.md) | comments 294 | [Discussion](discussions.md) | discussions 295 | [Time Entry](entries.md) | entries 296 | [Expense](expenses.md) | expenses 297 | [File](files.md) | files 298 | [Milestone](milestones.md) | milestones 299 | [Report](reports.md) | reports 300 | -------------------------------------------------------------------------------- /sections/tasks.md: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | * [Getting tasks](#list) 4 | * [Getting a task](#get) 5 | * [Creating a task](#create) 6 | * [Updating a task](#update) 7 | * [Changing the order of tasks](#update-tasks-order) 8 | * [Adding a file to a task](#add-file) 9 | * [Changing task workflow status](#status) 10 | * [Changing task priority](#priority) 11 | * [Changing the order of subtasks](#update-subtasks-order) 12 | * [Deleting a task](#delete) 13 | * [Task billing](#billing) 14 | * [The task object](#object) 15 | * [Dependent objects](#dependencies) 16 | 17 | 18 | ## Getting tasks 19 | 20 | You can list tasks by making a GET request to: 21 | 22 | * `/api/tasks` for a list of all task from all projects 23 | * `/api/tasks?where=project_id=[PROJECT_ID]` for a list of task from a project 24 | * `/api/tasks?where=tasklist_id=[TASKLIST_ID]` for a list of task from a task list 25 | * `/api/tasks?where=complete=false and project_id=[PROJECT_ID]` for a list of incomplete tasks from a project 26 | * `/api/tasks?where=users=[USER_ID]` for a list tasks assigned to a user 27 | * `/api/tasks?where=users in ([USER1_ID],[USER2_ID])` for a list tasks assigned to USER1 or USER2 28 | * `/api/tasks?where=users=anyone` for a list tasks that have no users assigned 29 | * `/api/tasks?where=users in ("anyone",[USER1_ID])` for a list tasks that have no users assigned or are assigned to USER1 30 | * `/api/tasks?where=users<>anyone` for a list tasks that have some user assigned 31 | * `/api/tasks?where=mytasks=true` for a list of "My Tasks", those are tasks assigned to the user making the request, or tasks not assigned to any specific user, tasks being limited only to the projects the user is assigned to. 32 | 33 | Example of response: 34 | 35 | ```json 36 | { 37 | "tasks": [ 38 | { 39 | "id": 2158, 40 | "name": "Design", 41 | "code": "PD-16", 42 | "project_id": 1, 43 | "tasklist_id": 2, 44 | "user_id": 1, 45 | "complete": false, 46 | "billable": true, 47 | "seq": 1, 48 | "description": "", 49 | "price_per_hour": null, 50 | "due_date": null, 51 | "budget_hours": null, 52 | "users": [ 53 | 45 54 | ], 55 | "created_on": "2014-07-25T11:16:24Z", 56 | "updated_on": "2014-10-13T14:22:53Z" 57 | }, 58 | { 59 | "id": 3320, 60 | "name": "Testing", 61 | "code": "PD-17", 62 | "project_id": 1, 63 | "tasklist_id": 3, 64 | "user_id": 2, 65 | "complete": true, 66 | "billable": true, 67 | "seq": 2, 68 | "description": "", 69 | "price_per_hour": null, 70 | "due_date": "2014-10-30", 71 | "budget_hours": null, 72 | "users": [], 73 | "created_on": "2014-07-25T11:18:11Z", 74 | "updated_on": "2014-08-23T14:22:05Z" 75 | } 76 | ] 77 | } 78 | ``` 79 | 80 | You can also [include related content](includes.md) when listing tasks. 81 | 82 | 83 | ## Getting a task 84 | 85 | To get the task info, make a GET request to: 86 | 87 | * `/api/tasks/[TASK_ID]` 88 | * `/api/tasks/[TASK_ID]?include=thread.comments` for task info with a list of comments to it 89 | * `/api/tasks/[TASK_ID]?include=files` for task info with a list of attached files 90 | 91 | Example response: 92 | 93 | ```json 94 | { 95 | "tasks": [ 96 | { 97 | "id": 2158, 98 | "name": "Design", 99 | "code": "PD-12", 100 | "project_id": 1, 101 | "tasklist_id": 2, 102 | "user_id": 1, 103 | "complete": false, 104 | "billable": true, 105 | "seq": 1, 106 | "description": "", 107 | "price_per_hour": null, 108 | "due_date": null, 109 | "budget_hours": null, 110 | "users": [ 111 | 45 112 | ], 113 | "created_on": "2014-07-25T11:16:24Z", 114 | "updated_on": "2014-10-13T14:22:53Z" 115 | } 116 | ] 117 | } 118 | ``` 119 | 120 | You can also [include related content](includes.md) when getting a tasks. 121 | 122 | 123 | ## Creating a task 124 | 125 | To create a task, make a POST request to: 126 | 127 | * `/api/tasks` 128 | 129 | with the request body containing the new task info, as in the example below: 130 | 131 | ```json 132 | { 133 | "name": "Logo Design", 134 | "description": "Please read the emails", 135 | "tasklist_id": 546, 136 | "users": [ 204 ] 137 | } 138 | ``` 139 | 140 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new task. The response body will contain the new task info as in the **Getting a task** section. 141 | 142 | When `project_id` is provided and `tasklist_id` is not, the task will be added to the `Default Task List`. In case there is no task list named `Default Task List`, such a task list will be created. 143 | 144 | ### Required fields 145 | 146 | When creating a task: `name`, `tasklist_id` or `project_id` 147 | 148 | 149 | ## Updating a task 150 | 151 | To update an existing task, make a POST or PUT request to: 152 | 153 | * `/api/tasks/[TASK_ID]` 154 | 155 | with the request body containing the updated info. You can send only the changed fields. 156 | 157 | Example of request body if you want to change the task name: 158 | 159 | ```json 160 | { 161 | "name": "Changes to logo" 162 | } 163 | ``` 164 | 165 | The response will return `200 OK` and will contain the updated task info as in the **Getting a task** section. 166 | 167 | 168 | ## Changing the order of tasks 169 | 170 | To reorder the tasks in a task list you need to make an **[update task list](tasklists.md#update-tasks-order)** request with a body similar to: 171 | 172 | ```json 173 | { 174 | "tasks_order": [ 39, 2, 10, 9, 11, 12, 209 ] 175 | } 176 | ``` 177 | 178 | where `tasks_order` is a list of task ids (from the task list) in the new order. 179 | 180 | You can change the order of a subset of tasks by sending only the list of task ids that changed their position. 181 | 182 | 183 | ## Adding a file to a task 184 | 185 | To add a file to a task, make a POST request to: 186 | 187 | * `/api/tasks/[TASK_ID]` 188 | 189 | The request content-type should be `multipart-form-data` and the file field name equal to `file`. Here's an example using `curl` command line: 190 | 191 | ```shell 192 | curl -u email:password 193 | -H 'Accept: application/json' 194 | -F "file=@screenshot.png" 195 | https://app.paymoapp.com/api/tasks/12345 196 | ``` 197 | 198 | The file can also be added when creating the task. In that case, all the task fields should be send in `multipart-form-data` format together with the file. 199 | 200 | 201 | ## Changing task workflow status 202 | 203 | To change task's status, make an update request with the following request body: 204 | 205 | ```json 206 | { 207 | "status_id": WORKFLOW_STATUS_ID 208 | } 209 | ``` 210 | 211 | The list of available task statuses can be obtained from the project workflow. 212 | 213 | **NOTE** When a task is marked as completed, the task workflow status is changed to "Complete" and when it is activated, the task workflow status is changed to "Backlog". 214 | 215 | 216 | ## Changing task priority 217 | 218 | To change task's priority, make an update request with the following request body: 219 | 220 | ```json 221 | { 222 | "priority": TASK_PRIORITY 223 | } 224 | ``` 225 | 226 | Allowed task priorities: 100, 75, 50, 25. 227 | * 100 - Critical 228 | * 75 - High 229 | * 50 - Normal 230 | * 25 - Low 231 | 232 | **NOTE** All tasks are created with Normal (50) priority if not specified otherwise 233 | 234 | 235 | ## Changing the order of subtasks 236 | 237 | To reorder the subtasks in a task you need to make an **update task** request with a body similar to: 238 | 239 | ```json 240 | { 241 | "subtasks_order": [ 39, 2, 10, 9, 11 ] 242 | } 243 | ``` 244 | 245 | where `subtasks_order` is a list of subtask ids in the new order. 246 | 247 | 248 | ## Deleting a task 249 | 250 | To delete a task, make a DELETE request to: 251 | 252 | * `/api/tasks/[TASK_ID]` 253 | 254 | If successful, the response will have a `200 OK` status code. 255 | 256 | ### Warning 257 | 258 | **Deleting a task will also delete all time entries logged for that task!** 259 | 260 | 261 | ## Task billing 262 | 263 | Tasks from billable projects (flat rate projects and time & materials projects) can be: 264 | * time based 265 | * flat rate 266 | * non-billable 267 | 268 | ### Time based tasks 269 | 270 | Defining fields for time based tasks: 271 | * `billable` will be `true` 272 | * `flat_billing` will be `false` 273 | * `price_per_hour` will be the task hourly rate. See project's [`hourly_billing_mode`](projects.md#billing) for how the actual hourly rate is selected. 274 | 275 | Example request to create a time based task: 276 | 277 | * POST `/api/tasks` with the body: 278 | 279 | ```json 280 | { 281 | "name": "Time based task", 282 | "billable": true, 283 | "flat_billing": false, 284 | "price_per_hour": 50.00, 285 | "tasklist_id": 1234 286 | } 287 | ``` 288 | 289 | ### Flat rate tasks 290 | 291 | Defining fields for time based tasks: 292 | * `billable` will be `true` 293 | * `flat_billing` will be `true` 294 | * `estimated_price` will be the task flat rate 295 | 296 | Example request to create a flat rate task: 297 | 298 | * POST `/api/tasks` with the body: 299 | 300 | ```json 301 | { 302 | "name": "Flat rate task", 303 | "billable": true, 304 | "flat_billing": true, 305 | "estimated_price": 100.00, 306 | "tasklist_id": 1234 307 | } 308 | ``` 309 | 310 | See also [project billing](projects.md#billing). 311 | 312 | 313 | ## The task object 314 | 315 | A task object has the following attributes: 316 | 317 | Attribute|Type|Description 318 | ---------|----|----------- 319 | id | integer | _(read-only)_ Unique task identifier 320 | name | text | Task name 321 | code | text | | _(read-only)_ | Task code format -. 322 | project_id | integer | _(read-only)_ Project id 323 | tasklist_id | integer | Task list id 324 | seq | integer | Position (order) of the task in the task list 325 | description | text | Task description and notes 326 | complete | boolean | If `true` the task is marked as complete 327 | completed_on | integer | _(read-only)_ Date and time when the subtask was completed 328 | completed_by | integer | _(read-only)_ Id of the user that completed the subtask 329 | due_date | date | Task due date. If task is not completed and due date has passed, the task is overdue. 330 | user_id | integer | Id of the user who created the task 331 | users | list | List of user ids that are assigned to the task. If no users are assigned, anyone assigned to the project sees this task in "My Tasks". 332 | billable | boolean | Only for tasks from billable projects, if `true` the task is billable. See [billing](#billing). 333 | flat_billing | boolean | Only for tasks from time & materials projects, if `true` the task has a flat rate. See [billing](#billing). 334 | price_per_hour | decimal | For billable time based tasks, price per hour when billing the time for this task. Note: actual hourly rate used depends on project's `hourly_billing_mode`. See [project billing](projects.md#billing). 335 | budget_hours | decimal | Budget hours for the task 336 | estimated_price | decimal | For billable tasks, the estimated task price based on tracked time for time based tasks, and the task flat price for flat rate tasks. 337 | invoiced | boolean | For flat rate tasks, if `true`, the task was already invoiced. 338 | invoice_item_id | integer | For flat rate tasks, if set, the ID of the invoice line (part of the invoice for the task). 339 | priority | integer | Task priority. See [priority](#priority). 340 | status_id | integer | Task workflow status. See [task status](#status). 341 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the task was created 342 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the task was last updated 343 | 344 | 345 | ## Dependent objects 346 | 347 | The following object types can be used in [includes](includes.md): 348 | 349 | Object type|Include key|Relationship 350 | -----------|-----------|---- 351 | [Project](projects.md) | project | parent 352 | [Task List](tasklists.md) | tasklist | parent 353 | [User](users.md) | user | parent 354 | [Comments Thread](comments.md) | thread | parent 355 | [Time Entry](entries.md) | entries | child 356 | [Subtask](subtasks.md) | subtasks | child 357 | [Invoice Item](invoices.md) | invoiceitem | parent 358 | [Workflow Status](workflow_status.md) | workflowstatus | parent 359 | -------------------------------------------------------------------------------- /sections/project_templates.md: -------------------------------------------------------------------------------- 1 | # Project templates 2 | 3 | Project templates help you create projects with a predefined set of tasks. 4 | The relationship between project templates and projects is bidirectional. You can create new templates from existing projects, and you can create new projects from existing templates. 5 | 6 | A project template contains a set of task lists and tasks. Tasks from a template may also have additional data such as billing info, budget hours and assigned users. 7 | Other project related content such as files, discussions, comments are not present in a project template. 8 | 9 | * [Listing project templates](#list) 10 | * [Getting a project template](#get) 11 | * [Creating a project template](#create) 12 | * [Updating a project template](#update) 13 | * [Deleting a project template](#delete) 14 | * [Applying a template to an existing project](#apply) 15 | * [The project template object](#object) 16 | * [Dependent objects](#dependencies) 17 | 18 | 19 | ## Listing project templates 20 | 21 | You can list your templates by making a GET request to: 22 | 23 | * `/api/projecttemplates` for a list of all templates 24 | * `/api/projecttemplates?include=projecttemplatestasklists,projecttemplatestasklists.projecttemplatestasks` for a list of project templates with the task lists and tasks info also included 25 | 26 | Example of response: 27 | 28 | ```json 29 | { 30 | "project_templates":[ 31 | { 32 | "id":16137, 33 | "name":"Billboard design", 34 | "created_on":"2015-10-16T12:22:06Z", 35 | "updated_on":"2015-10-16T12:22:06Z" 36 | }, 37 | { 38 | "id":16134, 39 | "name":"Flyer design", 40 | "created_on":"2015-10-16T12:21:02Z", 41 | "updated_on":"2015-10-16T12:21:02Z" 42 | } 43 | ] 44 | } 45 | ``` 46 | 47 | 48 | ## Getting a project template 49 | 50 | To get the project template info, make a GET request to: 51 | 52 | * `/api/projecttemplates/[TEMPLATE_ID]` for basic template info 53 | * `/api/projecttemplates/[TEMPLATE_ID]?include=projecttemplatestasklists,projecttemplatestasklists.projecttemplatestasks` for a complete info about the template, including task lists and tasks 54 | 55 | Example response: 56 | 57 | ```json 58 | { 59 | "project_templates":[ 60 | { 61 | "id":16137, 62 | "name":"Billboard design", 63 | "created_on":"2015-10-16T12:22:06Z", 64 | "updated_on":"2015-10-16T12:22:06Z" 65 | } 66 | ] 67 | } 68 | ``` 69 | 70 | An example response with task lists and tasks info included: 71 | 72 | ```json 73 | { 74 | "project_templates":[ 75 | { 76 | "id":16135, 77 | "name":"Advertising campaign", 78 | "created_on":"2015-10-16T12:21:15Z", 79 | "updated_on":"2015-10-16T12:21:15Z", 80 | "projecttemplatestasklists":[ 81 | { 82 | "id":39830, 83 | "name":"Social", 84 | "seq":0, 85 | "template_id":16135, 86 | "created_on":"2015-10-16T12:21:16Z", 87 | "updated_on":"2015-10-16T12:21:16Z", 88 | "projecttemplatestasks":[ 89 | { 90 | "id":155954, 91 | "name":"Trips", 92 | "billable":false, 93 | "seq":0, 94 | "description":"", 95 | "price_per_hour":250, 96 | "budget_hours":0, 97 | "tasklist_id":39830, 98 | "template_id":16135, 99 | "created_on":"2015-10-16T12:21:17Z", 100 | "updated_on":"2015-10-16T12:21:17Z", 101 | "users":[9028] 102 | }, 103 | { 104 | "id":155955, 105 | "name":"Meetings", 106 | "billable":true, 107 | "seq":1, 108 | "description":"", 109 | "price_per_hour":null, 110 | "budget_hours":0, 111 | "tasklist_id":39830, 112 | "template_id":16135, 113 | "created_on":"2015-10-16T12:21:17Z", 114 | "updated_on":"2015-10-16T12:21:17Z", 115 | "users":[] 116 | } 117 | ] 118 | } 119 | ] 120 | } 121 | ] 122 | } 123 | ``` 124 | 125 | 126 | ## Creating a project template 127 | 128 | There are two ways to create project templates: 129 | 130 | - from scratch. In this case, you have to add task lists and tasks yourself. 131 | - from an existing project. The template will contain all task lists and tasks from the project 132 | 133 | To create a project template, make a POST request to: 134 | 135 | * `/api/projecttemplates` 136 | 137 | with the request body containing the new template info. By default, this will create a project template with no task lists or tasks. 138 | 139 | Sample request body to create a template: 140 | 141 | ```json 142 | { 143 | "name": "Template for Advertising Campaigns" 144 | } 145 | ``` 146 | 147 | To create a template from an existing project, send the ID of the project as `project_id` in the request body. For example: 148 | 149 | ```json 150 | { 151 | "name": "Template for Advertising Campaigns", 152 | "project_id": 90033 153 | } 154 | ``` 155 | 156 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new project template. The response body will contain the new project template info as in the **Getting a project template** section. 157 | 158 | ### Required fields 159 | 160 | When creating a project template: `name`. 161 | For a complete description of all project template fields, see [project template object](#object). 162 | 163 | ### Adding task lists 164 | 165 | To add task lists to an existing project template, make a POST request to: 166 | 167 | * `/api/projecttemplatestasklists` 168 | 169 | with the request body containing the task list info. You have to send the ID of the template as `template_id` in the request body. 170 | 171 | Example of request body: 172 | 173 | ```json 174 | { 175 | "template_id": 16135, 176 | "name": "Design", 177 | "seq": 2 178 | } 179 | ``` 180 | 181 | Required fields when creating a task list: `name`, `template_id`. 182 | 183 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new task list. The response body will also contain the new task list info. 184 | 185 | ### Adding tasks 186 | 187 | To add tasks to an existing project template, make a POST request to: 188 | 189 | * `/api/projecttemplatestasks` 190 | 191 | with the request body containing the task info. You have to send the ID of the project template task list as `tasklist_id` in the request body. 192 | 193 | Example of request body: 194 | 195 | ```json 196 | { 197 | "tasklist_id": 39830, 198 | "name": "Copywriting", 199 | "description": "", 200 | "billable": true, 201 | "price_per_hour": 60, 202 | "budget_hours": 16, 203 | "seq": 1, 204 | "users": [9029] 205 | } 206 | ``` 207 | 208 | Required fields when creating a task list: `name`, `tasklist_id`. 209 | 210 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new task. The response body will also contain the new task info. 211 | 212 | 213 | ## Updating a project template 214 | 215 | To update an existing project template, make a POST or PUT request to: 216 | 217 | * `/api/projecttemplates/[TEMPLATE_ID]` 218 | 219 | with the request body containing the updated info. You can send only the changed fields. 220 | 221 | Example of request body if you want to change the template name: 222 | 223 | ```json 224 | { 225 | "name": "New template name" 226 | } 227 | ``` 228 | 229 | ### Updating a task list from a project template 230 | 231 | To update a task list from a project template, make a POST or PUT request to: 232 | 233 | * `/api/projecttemplatestasklists/[TASKLIST_ID]` 234 | 235 | with the request body containing the updated info. You can send only the changed fields. 236 | 237 | Example of request body if you want to change the task list name: 238 | 239 | ```json 240 | { 241 | "name": "New task list name" 242 | } 243 | ``` 244 | 245 | ### Updating a task from a project template 246 | 247 | To update a task from a project template, make a POST or PUT request to: 248 | 249 | * `/api/projecttemplatestasks/[TASK_ID]` 250 | 251 | with the request body containing the updated info. You can send only the changed fields. 252 | 253 | Example of request body if you want to change the task's name and the assigned users: 254 | 255 | ```json 256 | { 257 | "name": "New task name", 258 | "users": [9002, 9004] 259 | } 260 | ``` 261 | 262 | 263 | ## Deleting a project template 264 | 265 | Deleting a project template will also delete all the task lists and tasks from it, but it will have no effect on the projects that were created from this template. 266 | 267 | To delete a project template, make a DELETE request to: 268 | 269 | * `/api/projecttemplates/[TEMPLATE_ID]` 270 | 271 | If successful, the response will have a `200 OK` status code. 272 | 273 | ### Deleting a task list from a project template 274 | 275 | Deleting a task list from a project template will also delete all the tasks from this task list. 276 | 277 | To delete a task list, make a DELETE request to: 278 | 279 | * `/api/projecttemplatestasklists/[TASKLIST_ID]` 280 | 281 | If successful, the response will have a `200 OK` status code. 282 | 283 | ### Deleting a task from a project template 284 | 285 | To delete a task, make a DELETE request to: 286 | 287 | * `/api/projecttemplatestasks/[TASK_ID]` 288 | 289 | If successful, the response will have a `200 OK` status code. 290 | 291 | 292 | ## Applying a template to an existing project 293 | 294 | By applying a template to a project you can add all the tasks and task lists from the project template to the respective project. 295 | This will not replace or remove any existing tasks from the project. 296 | 297 | For more info about how to apply a project template to a project, see [Adding tasks from a project template](projects.md#from-template) in the Projects section. 298 | 299 | 300 | ## The project template object 301 | 302 | A project template object has the following attributes: 303 | 304 | Attribute|Type|Description 305 | ---------|----|----------- 306 | id | integer | _(read-only)_ Unique template identifier 307 | name | text | Template name 308 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the project template was created 309 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the project template was last updated 310 | 311 | ### The project template task list object 312 | 313 | Attribute|Type|Description 314 | ---------|----|----------- 315 | id | integer | _(read-only)_ Unique project template task list identifier 316 | name | text | Task list name 317 | seq | integer | Position (order) of the task list in the project template 318 | template_id | integer | _(read-only)_ Project template id 319 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the task list was created 320 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the task list was last updated 321 | 322 | ### The project template task object 323 | 324 | Attribute|Type|Description 325 | ---------|----|----------- 326 | id | integer | _(read-only)_ Unique task identifier 327 | name | text | Task name 328 | template_id | integer | _(read-only)_ Project template id 329 | tasklist_id | integer | Project template task list id 330 | seq | integer | Position (order) of the task in the task list 331 | description | text | Task description 332 | billable | boolean | If `true` the task is billable. 333 | budget_hours | decimal | Number of budget hours for the task 334 | price_per_hour | decimal | Price per hour 335 | users | list | List of user ids that are assigned to the task. 336 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the task was created 337 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the task was last updated 338 | 339 | 340 | ## Dependent objects 341 | 342 | The following object types can be used in [includes](includes.md): 343 | 344 | ### For project templates 345 | 346 | Object type|Include key|Relationship 347 | -----------|-----------|---- 348 | [Project template task list](projecttemplates.md) | projecttemplatetasklists | child 349 | [Project template task](projecttemplates.md) | projecttemplatetasks | child 350 | 351 | ### For project templates task lists 352 | 353 | Object type|Include key|Relationship 354 | -----------|-----------|---- 355 | [Project template](projecttemplates.md) | projecttemplate | parent 356 | [Project template task](projecttemplates.md) | projecttemplatetasks | child 357 | 358 | ### For project templates tasks 359 | 360 | Object type|Include key|Relationship 361 | -----------|-----------|---- 362 | [Project template](projecttemplates.md) | projecttemplate | parent 363 | [Project template task list](projecttemplates.md) | projecttemplatetasklist | parent 364 | -------------------------------------------------------------------------------- /sections/estimate_templates.md: -------------------------------------------------------------------------------- 1 | # Estimate templates 2 | 3 | Estimate templates define how an estimate looks like. That includes the layout, positioning of elements, as well as some texts and labels. 4 | Paymo offers a template gallery for estimates. These include different layouts in different languages. You can import templates from the gallery into your company account from Paymo company settings page, Invoicing sub-section. 5 | 6 | * [Listing estimate templates](#list) 7 | * [Listing estimate templates from Paymo library](#list-gallery) 8 | * [Getting an estimate template](#get) 9 | * [Creating an estimate template](#create) 10 | * [Updating an estimate template](#update) 11 | * [Deleting an estimate template](#delete) 12 | * [The estimate template object](#object) 13 | * [Dependent objects](#dependencies) 14 | 15 | 16 | ## Listing estimate templates 17 | 18 | By default, your Paymo account will have two estimate templates imported from the templates library. 19 | 20 | You can list your templates by making a GET request to: 21 | 22 | * `/api/estimatetemplates` 23 | 24 | Example of response: 25 | 26 | ```json 27 | { 28 | "estimatetemplates": [ 29 | { 30 | "id": 1230, 31 | "name": "English - Client on right", 32 | "title": "INVOICE", 33 | "is_default": false, 34 | "html": "<>", 35 | "css": "<>", 36 | "created_on": "2014-11-19T09:45:32Z", 37 | "updated_on": "2014-11-19T09:45:32Z" 38 | }, 39 | { 40 | "id": 1229, 41 | "name": "English - Client on left", 42 | "title": "INVOICE", 43 | "is_default": true, 44 | "html": "<>", 45 | "css": "<>", 46 | "created_on": "2014-11-19T09:45:32Z", 47 | "updated_on": "2015-02-26T14:17:48Z" 48 | } 49 | ] 50 | } 51 | ``` 52 | 53 | ## Listing estimate templates from Paymo library 54 | 55 | Templates from the library can be added to your account and used for your estimates. 56 | 57 | For a list of all available templates in the Paymo library, make a GET request to: 58 | 59 | * `/api/estimatetemplatesgallery` 60 | 61 | Example of response: 62 | 63 | ```json 64 | { 65 | "estimatetemplatesgallery": [ 66 | { 67 | "id": 1, 68 | "name": "English - Client on left", 69 | "title": "ESTIMATE", 70 | "html": "<>", 71 | "css": ">", 72 | "image": "https://app.paymoapp.com/assets/estimate-templates-gallery/a2aed6fbca9d292f3ec3d8859a66ef83.jpg", 73 | "created_on": "2014-10-29T13:38:25Z", 74 | "updated_on": "2014-11-19T15:38:31Z" 75 | }, 76 | { 77 | "id": 2, 78 | "name": "English - Client on right", 79 | "title": "ESTIMATE", 80 | "html": "<>", 81 | "css": ">", 82 | "image": "https://app.paymoapp.com/assets/estimate-templates-gallery/9ae0c5b134a5abdc73f3e25241cd93e8.jpg", 83 | "created_on": "2014-10-29T13:38:25Z", 84 | "updated_on": "2014-11-19T15:38:31Z" 85 | }, 86 | { 87 | "id": 5, 88 | "name": "Deutsch - Client on left", 89 | "title": "ANGEBOT", 90 | "html": "<>", 91 | "css": ">", 92 | "image": "https://app.paymoapp.com/assets/estimate-templates-gallery/bb5fd4a7298e9acd3400254b84826701.jpg", 93 | "created_on": "2014-11-03T09:16:28Z", 94 | "updated_on": "2014-11-19T15:38:31Z" 95 | }, 96 | { 97 | "id": 6, 98 | "name": "Deutsch - Client on right", 99 | "title": "ANGEBOT", 100 | "html": "<>", 101 | "css": ">", 102 | "image": "https://app.paymoapp.com/assets/estimate-templates-gallery/56d214e6c349dfe4cdd78558a2e5fd87.jpg", 103 | "created_on": "2014-11-03T09:19:47Z", 104 | "updated_on": "2014-11-19T15:38:31Z" 105 | }, 106 | { 107 | "id": 13, 108 | "name": "Español - Client on left", 109 | "title": "PRESUPUESTO", 110 | "html": "<>", 111 | "css": ">", 112 | "image": "https://app.paymoapp.com/assets/estimate-templates-gallery/5b2c9c0f702d1def37af2aeb1e9189b2.jpg", 113 | "created_on": "2014-11-03T09:58:51Z", 114 | "updated_on": "2014-11-19T15:38:31Z" 115 | }, 116 | { 117 | "id": 14, 118 | "name": "Español - Client on right", 119 | "title": "PRESUPUESTO", 120 | "html": "<>", 121 | "css": ">", 122 | "image": "https://app.paymoapp.com/assets/estimate-templates-gallery/816273056f7059918f7bde0d7f48c36a.jpg", 123 | "created_on": "2014-11-03T10:00:18Z", 124 | "updated_on": "2014-11-19T15:38:31Z" 125 | } 126 | ] 127 | } 128 | ``` 129 | 130 | 131 | ## Getting an estimate template 132 | 133 | To get the estimate template info, make a GET request to: 134 | 135 | * `/api/estimatetemplates/[TEMPLATE_ID]` 136 | 137 | Example response: 138 | 139 | ```json 140 | { 141 | "estimatetemplates": [ 142 | { 143 | "id": 1230, 144 | "name": "English - Client on right", 145 | "is_default": false, 146 | "html": "
\r\n\t
\r\n\t
\r\n\t\t
Provider
\r\n\t\t{company_info}\r\n\t
\r\n\t
\r\n\t\t
\r\n\t\t\t
Customer
\r\n\t\t\t\r\n\t\t
\r\n\t\t
{bill_to}
\r\n\t
\r\n\t
\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t
Estimate No.{number}
Date{date}
\r\n\t
\r\n\t
{title}
\r\n\t
\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t
ItemDescriptionPrice/UnitQuantityPrice
{item}{description}{price_unit}{quantity}{price}
\r\n\t
\r\n\t
\r\n\t\t
\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t
Subtotal{subtotal}
{tax_text}{tax}{tax_value}
{tax2_text}{tax2}{tax2_value}
Total{total}
\r\n\t\t
\r\n\t\t\r\n\t
\r\n\t
{footer}
\r\n\t
Sent using paymoapp.com
\r\n
", 147 | "css": "", 148 | "created_on": "2014-11-19T09:45:32Z", 149 | "updated_on": "2014-11-19T09:45:32Z" 150 | } 151 | ] 152 | } 153 | ``` 154 | 155 | 156 | ## Creating an estimate template 157 | 158 | To create an estimate template, make a POST request to: 159 | 160 | * `/api/estimatetemplates` 161 | 162 | with the request body containing the new template info. 163 | 164 | Sample request body to create a template with a custom estimate title color: 165 | 166 | ```json 167 | { 168 | "name": "Template with custom title color", 169 | "is_default": false, 170 | "html": "<>", 171 | "css": "#estimate-title { color: #999; }" 172 | } 173 | ``` 174 | 175 | The `html` attribute contains an HTML template code with special placeholders for key estimate elements. 176 | Placeholders look like `{PLACEHOLDER_NAME}` and will be replace with corresponding estimate property. 177 | There are also blocks of HTML code with special meanings. These look like ``. 178 | 179 | The `css` attribute contains _additional_ CSS rules to apply to estimate HTML template code. This is the field we recommend you use to customize how the estimate will look. 180 | 181 | Because the estimate template is a complex structure, we recommend to use an existing `html` content and add any desired changes through the `css` field. 182 | 183 | If successful, the response will return `201 Created`. The response header `Location` will contain a link for the new estimate template. The response body will contain the new estimate template info as in the **Getting an estimate template** section. 184 | 185 | ### Required fields 186 | 187 | When creating an estimate template: `name`. 188 | For a complete description of all estimate template fields, see [estimate template object](#object). 189 | 190 | 191 | ## Updating an estimate template 192 | 193 | To update an existing estimate template, make a POST or PUT request to: 194 | 195 | * `/api/estimatetemplates/[TEMPLATE_ID]` 196 | 197 | with the request body containing the updated info. You can send only the changed fields. 198 | 199 | Example of request body if you want to hide the footer from the estimate: 200 | 201 | ```json 202 | { 203 | "css": "#footer { display: none; }" 204 | } 205 | ``` 206 | 207 | ### Changing the default estimate template 208 | 209 | One of your estimate template is the default template used when creating a new estimate, of when displaying an estimate that has no template set. 210 | 211 | You can change the default estimate template by making an update request for the new default template with the `is_default` attribute set to `true`. 212 | 213 | Example of request body: 214 | 215 | ```json 216 | { 217 | "is_default": true 218 | } 219 | ``` 220 | 221 | The old default estimate template will have the `is_default` set to `false`. 222 | 223 | 224 | ## Deleting an estimate template 225 | 226 | You can only delete an estimate template if there are no estimates using this template and the template is not the default one. 227 | 228 | To delete an estimate template, make a DELETE request to: 229 | 230 | * `/api/estimatetemplates/[TEMPLATE_ID]` 231 | 232 | If successful, the response will have a `200 OK` status code. 233 | 234 | 235 | ## The estimate template object 236 | 237 | An estimate template object has the following attributes: 238 | 239 | Attribute|Type|Description 240 | ---------|----|----------- 241 | id | integer | _(read-only)_ Unique template identifier 242 | name | text | Template name 243 | title | text | Default estimate title 244 | html | text | HTML template code 245 | css | text | Additional CSS code (basic CSS rules for estimates are already included by Paymo) 246 | is_default | boolean | If `true`, this template is the default estimate template for your company 247 | created_on | [datetime](datetime.md) | _(read-only)_ Date and time when the estimate template was created 248 | updated_on | [datetime](datetime.md) | _(read-only)_ Date and time when the estimate template was last updated 249 | 250 | 251 | ## Dependent objects 252 | 253 | The following object types can be used in [includes](includes.md): 254 | 255 | Object type|Include key|Relationship 256 | -----------|-----------|---- 257 | [Estimate](estimates.md) | estimates | child 258 | --------------------------------------------------------------------------------