Targetprocess

The Targetprocess Developer Hub

Welcome to the Targetprocess developer hub. Here you'll find comprehensive guides and documentation to help you start working with Targetprocess as quickly as possible and support you if you get stuck. Let's jump right in!

Docs

Overview

API v2 is a read-only REST-like API which lets you query data in your Targetprocess account.

Important notice

API v2 is primarily intended for internal usage and integration between various components of Targetprocess. You can use it for your own purposes but please keep in mind that unlike API v1 we don't provide any backwards or forwards compatibility guarantees of API v2 between different Targetprocess versions. It also may be changed at any moment without any notice to meet the new requirements of Targetprocess itself.

This all means that you play with API v2 at your own risk. Don't hold us responsible for any future damage. Consider using API v1 for more stable public surface.

Url format

/api/v2/{entity}/{id}?
where={where}&select={select}&result={result}&
take={take}&skip={skip}&orderBy={orderby}&
callback={callback}&filter={filter}
  • {entity} - entity type name in single case (Userstory, Bug) or plural case (Userstories, Bugs) - see here for the list of available entity types
  • {id} - optional entity id. If specified, the response will contain only one item in {items} collection
  • {where} - filter expression over collection of entities
  • {select} - select projection.
  • {result} - final expression, used for aggregations' calculation on root entity.
  • {skip}, {take} - paging parameters, how many entities skip and take. If the result contains more entities, the response will have fields "next" or/and "prev" links to another pages.
  • {orderBy} - orderings
  • {callback} - JSONP callback
  • {filter} - a DSL Filter or Simple Filter like on boards. filter and where clauses are combined with and operator.

Also you can add following falgs to query string to modify output:

  • prettify - to prettify response json with spaces and new lines
  • isoDate - to get the dates in ISO 8601 format

Selectors


selector = expression | objectExpression

objectExpression = { namedSelectorList }

namedSelectorList = namedSelector [,namedSelectorList ]

namedSelector = field | name:expression | functionCall

functionCall = A call to Enumerable methods (see below)

expression = field | dlinqExpression

field = Any field of a referenced entity (get from here) or projected object

dlinqExpression = A valid .NET expression with some extensions (see below)


Every objectExpresssion will be represented as JSON object with given properties calculated from namedSelectors. For instance:

https://md5.tpondemand.com/api/v2/UserStory?select={id,storyName:name,project:{project.id,project.name},tasks.count()}

{
  "next": "http://md5.tpondemand.com/api/v2/UserStory?where=&select={id,storyName:name,project:{project.id,project.name},tasks.count()}&take=25&skip=25",
  "items": [
    {
      "id": 93114,
      "storyName": "Study Projectplace tool",
      "project": {
        "id": 37819,
        "name": "AVM: MKT_Backlog"
      },
      "tasks": 0
    },
    {
      "id": 93112,
      "storyName": "Briefly describe api v2",
      "project": {
        "id": 49105,
        "name": "TP3"
      },
      "tasks": 1
    },

If a name of the result filed is not defined, api V2 tries to inference the field name using a) field names (id -> id, project.id -> id), b) collection names (tasks.count() -> tasks), otherwise it will use name.

name should be a valid alphanumeric identifier: OK - id, camelCase, name1, name_with_underscore. Not OK - name with space, 1starting_from_number, name!with?symbols.

If you need to get values from inner entities, you need to specify full path to the property: project.id, feature.epic.name.

For example, I want to get user stories with id, name, effort, project (with id, name and process name), feature (with id, name and epic):

/api/v2/UserStory?select={id,name,project:{project.id,project.name,process:project.process.name},feature:{feature.id,feature.name,feature.epic}}

Let's format selector for convenience:

{
   id,
   name,
   project: { 
     project.id,
     project.name,
     process:project.process.name
   },
   feature: {
      feature.id,
      feature.name,
      feature.epic
   }
}

The result is:

   { // fully filled userstory
    "id": 93108,
    "name": "Add Left/Right arrows to scroll widgets library",
    "project": {
      "id": 49105,
      "name": "TP3",
      "process": "Kanban TP"
    },
    "feature": {
      "id": 49126,
      "name": "Dashboards MVF",
      "epic": { // as far as epic was requested as a field, it contains only "id" and "name"
        "id": 90678,
        "name": "Dashboards"
      }
    },
    {
      "id": 93114,
      "name": "Study Projectplace tool",
      "project": {
        "id": 37819,
        "name": "AVM: MKT_Backlog",
        "process": "Sales: General"
      },
      "feature": { // This UserStory has no feature, so result projection will include the empty "feature" object

      }
    }

Along with simple fields and related entities, you also can request inner collections with filters and projections and aggregations for inner collections. E.g. I want to get projects with:

  • all User Stories
  • all non-closed bugs
  • sum of feature efforts
  • count of opened requests

https://md5.tpondemand.com/api/v2/Project?select={id,name,userStories,openBugs:Bugs.Where(EntityState.IsFinal!=true),featureEffort:features.sum(effort),openRequestCount:requests.Count(EntityState.IsInitial==true)}

  • userstories - list of all User Stories in format {id,name}. As far it is just a field, it doesn't require explicit name
  • openBugs:Bugs.Where(EntityState.IsFinal!=true) - all non-closed bugs. We need to specify name of the result collection. As far as Bugs is a collection of bug we can use some standard Enumerable methods (see below)
  • featureEffort:features.sum(effort) - we can aggregate collections by some field of collection item.
  • openRequestCount:requests.Count(EntityState.IsInitial==true) - we can calculate counts with some filters.

Final selectors

It's possible to use aggregation on root collection.

Example: I want to get count of all user stories: userstories?result=Count

Result is:

81

Example: I want to get sum, average, min and max efforts from all user stories:

/api/v2/userstory?result={sum:sum(effort),average:average(effort),min:min(effort),max:max(effort)}

Result is:

{
  "sum": 1168.0000,
  "average": 14.419753,
  "min": 6.0000,
  "max": 29.0000
}

Example: I want to get effort sum from all user stories, that have tasks and ids > 50:

/api/v2/userstory?where=id%3E50%20AND%20Tasks.count!=0&result={sum:sum(effort)}

Result is:

{
  "sum": 259.0000
}

.NET Expressions

API v2 uses LINQ expression parser that use syntax that is very close to standard .NET syntax. There are some difference:

  • Casts are supported via Assignable.As<UserStory>. You can access any simple or reference field of a specicific type, like: Assignable.As<UserStory>.Epic.Name. Collections on casted entities are not supported!
  • You can use Convert class to convert strings to numbers or dates or vice versa.
  • Decimal values are converted to double(nullable double) automaticaly in case double and decimal values are used in one expression: result of it.Double + it.Decimal and it.NullableDouble * it.Decimal will be double and double?. You don't need to use Convert.ToDouble(it.Decimal). This also works for IFNONE/IIF operators: you can simply write IFNONE(it.NullableDouble, it.NullableDecimal) or IIF(it.NullableDecimal.HasValue, it.NullableDecimal.Value, it.Double).
  • Nullable bool is automaticaly converted to bool (false for null value). So if nullable bool is used in place where bool should be used it's usage will be converted from it.NullableBool to it.NullableBool == True.
  • Constructors are not supported, but you can construct your anonymous objects using syntax described above: {ex1,ex2}
  • You can use static methods of some types (DateTime, Convert, Guid). E.g. DateTime.Now, Convert.ToInt
  • You can use Nullable types and checks for nulls
  • You can use instance methods of simple types (e.g., object.ToString(), string.Length)
  • You can use IIF operator instead of a?b:c
  • You can use IFNONE operator: IFNONE(a,0) instead of a.HasValue?a.Value:0
  • You can use operators (-,+,*,/), comparisons (<,>,<=,>=,==), Boolean constants (true,false), string constants ("Some string"), chars ('c') and null identifier.
    • Do not forget that + sign is treated as space in URLs so do not forget to replace it with %2b.
  • You can use operator in (id in [1,2,3])

Also some limited subset of IQueryable methods are supported for nested collections (e.g. bugs of user story):

  • Select(selector) - projection as described above
  • Where(filter) - filters as described below
  • Sum(expression) (as well as Max, Min, Average) - aggregations over specified expression
  • Count(filter) - count of filtered items

Filters

Filters should be valid .NET expressions that returns true or false.

You can use identifier 'it' to reference the parameter of a filter. For example, I want list of all efforts greater than 5:

/api/v2/Project?select={id,name,efforts:userStories.Select(effort).Where(it%3E5)}
{
      "id": 35923,
      "name": "Articles",
      "efforts": [
        10.0000,
        21.0000
      ]
    },
    {
      "id": 70163,
      "name": "Astroport",
      "efforts": [

      ]
    },
    {
      "id": 53775,
      "name": "AVM: CFO",
      "efforts": [
        9.5000
      ]
    },

Ordering

Ascending and descending orders are available, ascending is default option.
To specify the order you should place 'asc' or 'desc' after selector.
To enable additional sort criteria to sort a sequence you can enumerate them using comma.

For example you can sort user stories descending by effort and then ascending by name:

/api/v2/userstory?select={id,name,effort}&orderBy=effort desc, name

[
    {
        "id": 72903,
        "name": "Defiant",
        "effort": 120
    },
    {
        "id": 86582,
        "name": "Nova",
        "effort": 50
    },
    {
        "id": 71004,
        "name": "Rigel",
        "effort": 20
    },
    {
        "id": 94066,
        "name": "Springfield",
        "effort": 20
    }
]

Response format

Response is JSON object with requested collection in items field. For instance:

{
  "next": "/api/v2/UserStory?where=&select={id,name,endDate:(endDate-StartDAte).TotalDays}&take=25&skip=25&type=UserStory",
  "items": [
    {
      "id": 93110,
      "name": "Widgets API documentation"
    }
  ]
}

All fields are in camelCase, all null values are omitted.

Custom fields

There are two possibilities to get custom fields from api v2:

  • CustomValues["Custom Field Name"] - the response will include custom field value only
  • CustomValues.Get("Custom Field Name") - the response will include custom field value with some info about the field.

For instance, we have Date custom field named DateCF defined for UserStory:

/api/v2/UserStory?select={id,dateCF:CustomValues.Get("DateCF"),DateCFRaw:CustomValues["DateCF"]}

Response:

{
  "items": [
    {
      "id": 22 // This UserStory has no DateCF custom field defined in a UserStory's Process, so there is no such filed in the returned item
    },
    {
      "id": 11,    // This UserStory has a custom field DateCF, but the field is empty, so the response
      "dateCF": {  // will include only dateCF item with name, type and entityKind, but without a value
        "name": "DateCF",
        "type": "Date",
        "entityKind": "UserStory"
      } // ... and will not unclide a dateCFRaw values because it is equal to null and null values are omited
    },
    {
      "id": 8,
      "dateCF": {
        "name": "DateCF",
        "type": "Date",
        "entityKind": "UserStory",
        "value": "\/Date(1415826000000+0300)\/"   // This userStory has DateCF field with value set
      },
      "dateCFRaw": "\/Date(1415826000000+0300)\/"
    }
  ]
}

API v2 is tolerate for custom fields - if there is no such custom field defined for the entity, API v2 will not fail, just no return the custom field value.

Custom fields are not supported in filters in general. The only supported filter kind is to check whether the cusotm field is set or not:

&where=CustomValues["DateCF"]!=null
&where=CustomValues["DateCF"]==null

Overview

API v2 is a read-only REST-like API which lets you query data in your Targetprocess account.