Are you using Harvest, Teamwork, JIRA, Asana, Toggl, … but missing some functionality like profits, revenues, etc…? Costlocker is now able to create and update projects via its API.

Updates

Project API introduction

We tried to unify the project structure as much as possible. If you’re familiar with /api-public/v2/projects/ endpoint it should be simple to execute any operation with project.

Project detail

Basic project information is available in the standard json format. More advanced concepts (personnel costs, billing, …) are defined in items.

{
   "id":"123456",
   "name":"Website",
   "client": "Google",
   "dates":{
      "date_start":"2017-03-20",
      "date_end":"2017-05-20"
   },
   "state": "running",
   "project_type": "standard",
   "currency": {
      "project": "EUR",
      "json": "USD"
   },
   "recurring": {
      "id": null,
      "settings": null
   },
   "project_id": {
      "id": null,
      "is_generated": false
   },
   "tags":["Billable"],
   "responsible_people": ["test@example.com"],
   "budget": {
      "type": "time_estimates.person_activity",
      "client_rate": "activity",
      "progress_type": null,
      "bill_exceeded_estimates": false
   },
   "items":[]
}

Recurring projects

Read-only! Not editable in API. E.g. you can’t create a new recurring project in API.

Recurring project example
{
   "id": 123,
   "name":"Parent project",
   "project_type": "recurring",
   "recurring": {
      "id": null,
      "settings": {
        "frequency": "monthly",
        "date_start": "2019-01-01",
        "date_end": null
      }
   },
   "links": {
     "project": "https://new.costlocker.com/api-public/v2/projects/123"
   }
}
Standard project created from recurring project
{
   "id": 456,
   "name":"Child project",
   "project_type": "standard",
   "recurring": {
      "id": 123,
      "settings": null
   },
   "links": {
     "project": "https://new.costlocker.com/api-public/v2/projects/456",
     "recurring_project": "https://new.costlocker.com/api-public/v2/projects/123"
   }
}

Other project settings

Budget

Items

Field items are used for defining personnel costs, project expenses, billing and discount. The returned item is always of the same format:

{
   "items":[
      {
         "action": "upsert",
         "item":{
            "type":"expense",
            "expense_id":"123457"
         },
         "expense":{
            "description":"Invision",
            "purchased":{
               "total_amount":500,
               "date":null
            },
            "billed":{
               "total_amount":500
            }
         }
      }
   ]
}

Personnel costs

You can define an activity, a person and a task. We recommend to create just leafs though. Activity budget represents an aggregation of personal budgets. The personal budget is an aggregation of tasks. This way, you can just create the leaf and the activity/person is upserted.

Activity

Every activity has a name and an hourly rate. The existing client rate is used if the hourly_rate is missing. If you create a new activity without an hourly rate, zero rate is used.

{
    "item": {
        "type": "activity",
        "activity_id": 123456
    },
    "activity": {
        "name": "Social media",
        "hourly_rate": 100
    }
}

Person

It is necessary to specify the email, names, role and salary for every new person. The role, salary and names are used only when creating a new person. An existing person is never updated!

{
    "item": {
        "type": "person",
        "activity_id": 123456,
        "person_id": 123456
    },
    "hours": {
        "budget": 5
    },
    "activity": {
        "name": "Social media",
        "hourly_rate": 20
    },
    "person": {
        "email": "manager@example.com",
        "first_name": "Monthly",
        "last_name": "Salary",
        "role": "MANAGER",
        "salary": {
            "payment": "monthly",
            "salary": 10000,
            "hours": 160,
            "date_start": "2017-02-01 10:00:00"
        }
    }
}

The same person definition can be used in responsible_people field.

Task

Tasks are assigned to a person and activity. A parent can be referenced by ids in the item or by a name/email in the relevant field. References are also described in shortcuts.

{
    "item": {
        "type": "task",
        "activity_id": 123456,
        "person_id": 123456,
        "task_id": 123456
    },
    "hours": {
        "budget": 20
    },
    "activity": {
        "name": "Social media",
        "hourly_rate": 20
    },
    "task": {
        "name": "new task"
    },
    "person": {
        "email": "employee@example.com",
        "first_name": "Hourly",
        "last_name": "Rate",
        "role": "ADMIN",
        "salary": {
            "payment": "hourly",
            "hourly_rate": 0
        }
    }
}

Project expenses

Partial updates are supported. Please see the example in billing.

{
    "item": {
        "type": "expense",
        "expense_id": 123457
    },
    "expense": {
        "description": "new description",
        "purchased": {
            "total_amount": 1000,
            "date": "2017-05-15"
        },
        "billed": {
            "total_amount": 500
        }
    }
}

Discount

Every project has one discount. No ids are used, there is only one amount that needs to be adjusted.

{
    "item": {
        "type": "discount"
    },
    "discount": {
        "total_amount": 100
    }
}

Billing

Be aware that revenue (personnel costs + project expenses - discount) cannot be smaller than billing.

{
    "item": {
        "type": "billing"
    },
    "billing": {
        "description": "INV201705150001",
        "total_amount": 900,
        "date": "2017-05-15",
        "status": "draft"
    }
}

You can execute partial updates, such as changing the billing status without modifying the amount…

{
    "item": {
        "type": "billing",
        "billing_id": 123456
    },
    "billing": {
        "status": "sent"
    }
}

Budgets

We support new budget types since April 2018:

type Description
time_estimates.person_activity Activity hourly rate * Person hours estimates
time_estimates.activity Activity hourly rate * Activity hours estimate
timesheet Activity hourly rate * Tracked hours
no_budget All tracked hours are non-billable
fixed_price.activity Activity fixed price
fixed_price.project Project fixed price

Below you can see what fields are required for each budget type with activity hourly rate. You can send for example person hours budget to timesheet budget, but we’ll ignore it (webhook would contain zero hours budget).

Budget fields - activity client rate

Required fields for person hourly rate:

Budget fields - person client rate

You don’t have to worry about different client rate, if you create just leafs!

Person estimates (time_estimates.person_activity)

Budget is defined in person and activity item. Take a look at personnel costs.

{
    "item": {
        "type": "person",
        "activity_id": 123456,
        "person_id": 123456
    },
    "activity": {
        "name": "Social media",
        "hourly_rate": 20
    },
    "hours": {
        "budget": 5
    },
    "person": "manager@example.com"
}

Activity estimates (time_estimates.activity)

Budget is defined in activity item:

{
    "item": {
        "type": "activity",
        "activity_id": 123456
    },
    "activity": {
        "name": "Social media",
        "hourly_rate": 100
    },
    "hours": {
        "budget": 20
    }
}

Timesheet (timesheet)

Budget is defined in activity item:

{
    "item": {
        "type": "activity",
        "activity_id": 123456
    },
    "activity": {
        "name": "Social media",
        "hourly_rate": 100
    }
}

No budget (no_budget)

Hourly rate or budget amount is ignored. Tracked time is never billed!

{
    "item": {
        "type": "activity",
        "activity_id": 123456
    },
    "activity": {
        "name": "Social media"
    }
}

Activity fixed price (fixed_price.activity)

Budget is defined in activity item:

{
    "item": {
        "type": "activity",
        "activity_id": 123456
    },
    "activity": {
        "name": "Social media"
    },
    "budget": {
        "total_amount": 2000
    }
}

Project fixed price (fixed_price.project)

Budget is defined in project item:

{
    "item": {
        "type": "project"
    },
    "budget": {
        "total_amount": 2000
    }
}

Progress budget

Add hours budget if project progress is tracked with time estimates. Add it to person item if you’re using time_estimates.person_activity, add it to activity item if time_estimates.activity.

{
    "hours": {
        "budget": 20
    }
}

Multi-currency

Project has one currency defined in currency.project. The second currency.json avoids BC in API, since we had supported only one currency per company.

{
   "currency": {
      "project": "EUR",
      "json": "USD"
   }
}

Example

Company currency

By default all responses have amounts in company currency, in our case it’s USD.

{
   "currency": {
      "project": "EUR",
      "json": "USD"
   },
    "items": [
        {
            "item": {
                "type": "project"
            },
            "budget": {
                "total_amount": 1000
            }
        }
    ],
    "links": {
       "project": "https://new.costlocker.com/api-public/v2/projects/123",
       "peoplecosts": "https://new.costlocker.com/api-public/v2/projects/123?types=peoplecosts",
       "change_currency": "https://new.costlocker.com/api-public/v2/projects/123?currency=project"
    }
}

Project currency - ?currency=project

Amounts are returned in project currency when query string ?currency=project is used.

{
   "currency": {
      "project": "EUR",
      "json": "EUR"
   },
    "items": [
        {
            "item": {
                "type": "project"
            },
            "budget": {
                "total_amount": 900
            }
        }
    ],
    "links": {
       "project": "https://new.costlocker.com/api-public/v2/projects/123?currency=project",
       "peoplecosts": "https://new.costlocker.com/api-public/v2/projects/123?currency=project&types=peoplecosts",
       "change_currency": "https://new.costlocker.com/api-public/v2/projects/123"
    }
}

Updating projects

Same rules are applied for creating/updating projects. We determine project currency from currency.project and amounts currency from currency.json. Query string ?currency=project is ignored!


API shortcuts

You can use shortcuts when creating a new project or experimenting with the API. This is an experimental feature for developers. If you don’t like it, you can use the standard formats returned by the API.

Shortcut Full representation Description
item item.type Item type definition
activity activity.name Activity referenced by name
person person.email Person referenced by email
task task.name Task referenced by name
hours hours.budget Hours budget
client client.id or client.name Client referenced by id or name
tag tag.id or rag.name Tag referenced by id or name

Let’s say you want to create a new task for an existing person:

{
   "name":"Shortcuts experiment",
   "client":"Google",
   "responsible_people":[
      "existing-perso@example.com",
      {
         "email":"manager@example.com"
      }
   ],
   "tags":[
      "social",
      {
         "name":"Billable"
      }
   ],
   "items":[
      {
         "item":"task",
         "task":"new task",
         "activity":"Social media",
         "person":"existing-person@example.com",
         "hours":5
      }
   ]
}

Be aware that shortcuts are only used in requests. A response always contains standard item definition.

{
   "id": "2707",
   "items":[
      {
         "action":"upsert",
         "item":{
            "type":"task",
            "activity_id":579,
            "person_id":123456,
            "task_id":"855"
         }
      }
   ]
}

Demo: Import projects from Harvest

We’ve built Harvest projects importer during development of the new API, try it at https://harvest.integrations.costlocker.com (source code).

Let us know if you’ve created similar application that connects Costlocker to a project management tool!

Import harvest projects to Costlocker


Did you find a mistake? Is something unclear or isn’t the code working? Help us to improve the article!