API: Relations
Work packages may be related to each other in different ways.
+--------------+ +--------------+
| | 1 1 | |
| Work package +-------------+--------------+ Work package |
| | from | to | |
+--------------+ | +--------------+
+------+-------+
| Relation |
+--------------+
| type |
| reverseType |
| description |
| lag |
+--------------+
Actions
Link | Description | Condition |
---|---|---|
update | Updates the relation between two work packages via a form | Permission: manage work package relations |
updateImmediately | Updates the relation between two work packages | Permission: manage work package relations |
delete | Destroys the relation between the two work packages | Permission: manage work package relations |
Linked Properties
Link | Description | Type | Constraints | Supported operations | Condition |
---|---|---|---|---|---|
self | This relation | Relation | not null | READ | Permission: view work packages |
schema | The schema of this relation | Schema | not null | READ | |
from | The emanating work package | WorkPackage | not null | READ | Permission: view work packages |
to | The work package the relation ends in | WorkPackage | not null | READ | Permission: view work packages |
Local Properties
Property | Description | Type | Constraints | Supported operations |
---|---|---|---|---|
id | Relation ID | Integer | x > 0 | READ |
name | The internationalized name of this kind of relation | String | READ | |
type | Which kind of relation (blocks, precedes, etc.) | String | in: relates, duplicates, duplicated, blocks, blocked, precedes, follows, includes, partof, requires, required | READ / WRITE |
reverseType | The kind of relation from the other WP’s perspective | String | in: relates, duplicates, duplicated, blocks, blocked, precedes, follows, includes, partof, requires, required | READ |
description | Short text further describing the relation | String | READ / WRITE | |
lag* | The number of days between closing of from and start of to | Integer | x >= 0 | READ / WRITE |
* Only applicable for some relation types such as “follows”. You can check using the relation by schema endpoint at /api/v3/relations/schema/{type}
.
Methods
List relations
Lists all relations according to the given (optional, logically conjunctive) filters and ordered by ID. The response only includes relations between work packages which the user is allowed to see.
filters
string
optional query
JSON specifying filter conditions. Accepts the same format as returned by the queries endpoint. Valid fields to filter by are:
- id - ID of relation
- from - ID of work package from which the filtered relations emanates.
- to - ID of work package to which this related points.
- involved - ID of either the
from
or theto
work package. - type - The type of relation to filter by, e.g. “follows”.
Example:[{ "from": { "operator": "=", "values": 42 }" }]
sortBy
string
optional query
JSON specifying sort criteria. Accepts the same format as returned by the queries endpoint.
Example:[["type", "asc"]]
200
OK
{
"_type": "Collection",
"total": 40,
"count": 20,
"pageSize": 20,
"offset": 1,
"_embedded": [
{
"_type": "Relation",
"id": 650,
"name": "duplicated by",
"type": "duplicated",
"reverseType": "duplicates",
"lag": null,
"description": "Those are the same... stupid Stormtroopers!",
"_links": {
"self": {
"href": "/api/v3/relations/650"
},
"updateImmediately": {
"href": "/api/v3/relations/650",
"method": "patch"
},
"delete": {
"href": "/api/v3/relations/650",
"method": "delete,",
"title": "Remove relation"
},
"from": {
"href": "/api/v3/work_packages/108",
"title": "Destroy Rebel Base"
},
"to": {
"href": "/api/v3/work_packages/78",
"title": "Annihilate Yavin 4"
}
}
},
{
"_type": "Relation",
"id": 652,
"name": "relates to",
"type": "relates",
"reverseType": "relates",
"_hint": "Relation resource shortened for brevity."
},
{
"_hint": "Further relation resources shortened for brevity."
}
],
"_links": {
"self": {
"href": "/api/v3/relations?filters=%5B%5D&offset=1&pageSize=20"
},
"jumpTo": {
"href": "/api/v3/relations?filters=%5B%5D&offset=%7Boffset%7D&pageSize=20",
"templated": true
},
"changeSize": {
"href": "/api/v3/relations?filters=%5B%5D&offset=1&pageSize=%7Bsize%7D",
"templated": true
},
"nextByOffset": {
"href": "/api/v3/relations?filters=%5B%5D&offset=2&pageSize=20"
}
}
}
RelationCollectionModel
{
"allOf": [
{
"$ref": "#/components/schemas/CollectionModel"
},
{
"type": "object",
"required": [
"_links",
"_embedded"
],
"properties": {
"_links": {
"type": "object",
"required": [
"self"
],
"properties": {
"self": {
"allOf": [
{
"$ref": "#/components/schemas/Link"
},
{
"description": "This relation collection\n\n**Resource**: RelationCollectionReadModel"
}
]
}
}
},
"_embedded": {
"type": "object",
"required": [
"elements"
],
"properties": {
"elements": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RelationModel"
}
}
}
}
}
}
]
}
400
Returned if the client provides invalid filter parameters.
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:InvalidQuery",
"message": [
"Filters Type filter has invalid values."
]
}
ErrorResponse
{
"type": "object",
"required": [
"_type",
"errorIdentifier",
"message"
],
"properties": {
"_embedded": {
"type": "object",
"properties": {
"details": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"example": "project"
}
}
}
}
},
"_type": {
"type": "string",
"enum": [
"Error"
]
},
"errorIdentifier": {
"type": "string",
"example": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation"
},
"message": {
"type": "string",
"example": "Project can't be blank."
}
}
}
Get relation
Get a single relation specified by its unique identifier.
id
integer
required path
Relation id
Example:1
200
OK
{
"_type": "Relation",
"id": 650,
"name": "duplicated by",
"type": "duplicated",
"reverseType": "duplicates",
"lag": null,
"description": "Those are the same... stupid Stormtroopers!",
"_embedded": {
"from": {
"id": 108,
"_type": "WorkPackage",
"_hint": "Work package resource shortened for brevity."
},
"to": {
"id": 78,
"_type": "WorkPackage",
"_hint": "Work package resource shortened for brevity."
}
},
"_links": {
"self": {
"href": "/api/v3/relations/650"
},
"updateImmediately": {
"href": "/api/v3/relations/650",
"method": "patch"
},
"delete": {
"href": "/api/v3/relations/650",
"method": "delete,",
"title": "Remove relation"
},
"from": {
"href": "/api/v3/work_packages/108",
"title": "Destroy Rebel Base"
},
"to": {
"href": "/api/v3/work_packages/78",
"title": "Annihilate Yavin 4"
}
}
}
RelationModel
{
"type": "object",
"required": [
"_type",
"id"
],
"properties": {
"_type": {
"type": "string",
"enum": [
"Relation"
]
},
"id": {
"type": "integer",
"description": "Relation ID"
},
"name": {
"type": "string",
"description": "The internationalized name of this type of relation"
},
"type": {
"type": "string",
"description": "The relation type.",
"enum": [
"relates",
"duplicates",
"duplicated",
"blocks",
"blocked",
"precedes",
"follows",
"includes",
"partof",
"requires",
"required"
]
},
"reverseType": {
"type": "string",
"description": "The type of relation from the perspective of the related work package.",
"enum": [
"relates",
"duplicates",
"duplicated",
"blocks",
"blocked",
"precedes",
"follows",
"includes",
"partof",
"requires",
"required"
]
},
"description": {
"type": [
"string",
null
],
"description": "A descriptive text for the relation."
},
"lag": {
"type": [
"integer",
null
],
"description": "The lag in days between closing of `from` and start of `to`",
"minimum": 0
},
"_embedded": {
"type": "object",
"properties": {
"from": {
"$ref": "#/components/schemas/Work_PackageModel"
},
"to": {
"$ref": "#/components/schemas/Work_PackageModel"
}
}
},
"_links": {
"type": "object",
"required": [
"self",
"schema",
"from",
"to"
],
"properties": {
"self": {
"allOf": [
{
"$ref": "#/components/schemas/Link"
},
{
"description": "This relation\n\n**Resource**: Relation\n\n# Conditions\n\n**Permission**: view work packages"
}
]
},
"updateImmediately": {
"allOf": [
{
"$ref": "#/components/schemas/Link"
},
{
"description": "Updates the relation between two work packages\n\n# Conditions\n\n**Permission**: manage work package relations"
}
]
},
"delete": {
"allOf": [
{
"$ref": "#/components/schemas/Link"
},
{
"description": "Destroys the relation between the two work packages\n\n# Conditions\n\n**Permission**: manage work package relations"
}
]
},
"from": {
"allOf": [
{
"$ref": "#/components/schemas/Link"
},
{
"description": "The emanating work package\n\n**Resource**: WorkPackage\n\n# Conditions\n\n**Permission**: view work packages"
}
]
},
"to": {
"allOf": [
{
"$ref": "#/components/schemas/Link"
},
{
"description": "The work package the relation ends in\n\n**Resource**: WorkPackage\n\n# Conditions\n\n**Permission**: view work packages"
}
]
}
}
}
},
"example": {
"_links": {
"self": {
"href": "/api/v3/relations/1"
},
"update": {
"href": "/api/v3/relations/1/form",
"method": "POST"
},
"updateImmediately": {
"href": "/api/v3/relations/1",
"method": "PATCH"
},
"delete": {
"href": "/api/v3/relations/1",
"method": "DELETE"
},
"from": {
"href": "/api/v3/work_packages/42",
"title": "Steel Delivery"
},
"to": {
"href": "/api/v3/work_packages/84",
"title": "Bending the steel"
}
},
"_type": "Relation",
"id": 1,
"name": "precedes",
"type": "precedes",
"reverseType": "follows",
"description": "We can't bend the steel before it's been delivered!",
"lag": 0
}
}
404
Returned if the relation does not exist or the client does not have sufficient permissions to see it.
Required permission: view work packages for the involved work packages
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:NotFound",
"message": "The specified relation does not exist."
}
ErrorResponse
{
"type": "object",
"required": [
"_type",
"errorIdentifier",
"message"
],
"properties": {
"_embedded": {
"type": "object",
"properties": {
"details": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"example": "project"
}
}
}
}
},
"_type": {
"type": "string",
"enum": [
"Error"
]
},
"errorIdentifier": {
"type": "string",
"example": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation"
},
"message": {
"type": "string",
"example": "Project can't be blank."
}
}
}
Update relation
When calling this endpoint the client provides a single object, containing the properties and links that it wants to change, in the body. It is only allowed to provide properties or links supporting the write operation.
Note that changing the type
of a relation invariably also changes the respective reverseType
as well as the “name” of it. The returned Relation object will reflect that change. For instance if you change a Relation’s type
to “follows” then the reverseType
will be changed to precedes
.
id
integer
required path
Relation ID
Example:1
200
OK
{
"_type": "Relation",
"id": 650,
"name": "duplicated by",
"type": "duplicated",
"reverseType": "duplicates",
"lag": null,
"description": "Those are the same... stupid Stormtroopers!",
"_embedded": {
"from": {
"id": 108,
"_type": "WorkPackage",
"_hint": "Work package resource shortened for brevity."
},
"to": {
"id": 78,
"_type": "WorkPackage",
"_hint": "Work package resource shortened for brevity."
}
},
"_links": {
"self": {
"href": "/api/v3/relations/650"
},
"updateImmediately": {
"href": "/api/v3/relations/650",
"method": "patch"
},
"delete": {
"href": "/api/v3/relations/650",
"method": "delete,",
"title": "Remove relation"
},
"from": {
"href": "/api/v3/work_packages/108",
"title": "Destroy Rebel Base"
},
"to": {
"href": "/api/v3/work_packages/78",
"title": "Annihilate Yavin 4"
}
}
}
RelationModel
{
"type": "object",
"required": [
"_type",
"id"
],
"properties": {
"_type": {
"type": "string",
"enum": [
"Relation"
]
},
"id": {
"type": "integer",
"description": "Relation ID"
},
"name": {
"type": "string",
"description": "The internationalized name of this type of relation"
},
"type": {
"type": "string",
"description": "The relation type.",
"enum": [
"relates",
"duplicates",
"duplicated",
"blocks",
"blocked",
"precedes",
"follows",
"includes",
"partof",
"requires",
"required"
]
},
"reverseType": {
"type": "string",
"description": "The type of relation from the perspective of the related work package.",
"enum": [
"relates",
"duplicates",
"duplicated",
"blocks",
"blocked",
"precedes",
"follows",
"includes",
"partof",
"requires",
"required"
]
},
"description": {
"type": [
"string",
null
],
"description": "A descriptive text for the relation."
},
"lag": {
"type": [
"integer",
null
],
"description": "The lag in days between closing of `from` and start of `to`",
"minimum": 0
},
"_embedded": {
"type": "object",
"properties": {
"from": {
"$ref": "#/components/schemas/Work_PackageModel"
},
"to": {
"$ref": "#/components/schemas/Work_PackageModel"
}
}
},
"_links": {
"type": "object",
"required": [
"self",
"schema",
"from",
"to"
],
"properties": {
"self": {
"allOf": [
{
"$ref": "#/components/schemas/Link"
},
{
"description": "This relation\n\n**Resource**: Relation\n\n# Conditions\n\n**Permission**: view work packages"
}
]
},
"updateImmediately": {
"allOf": [
{
"$ref": "#/components/schemas/Link"
},
{
"description": "Updates the relation between two work packages\n\n# Conditions\n\n**Permission**: manage work package relations"
}
]
},
"delete": {
"allOf": [
{
"$ref": "#/components/schemas/Link"
},
{
"description": "Destroys the relation between the two work packages\n\n# Conditions\n\n**Permission**: manage work package relations"
}
]
},
"from": {
"allOf": [
{
"$ref": "#/components/schemas/Link"
},
{
"description": "The emanating work package\n\n**Resource**: WorkPackage\n\n# Conditions\n\n**Permission**: view work packages"
}
]
},
"to": {
"allOf": [
{
"$ref": "#/components/schemas/Link"
},
{
"description": "The work package the relation ends in\n\n**Resource**: WorkPackage\n\n# Conditions\n\n**Permission**: view work packages"
}
]
}
}
}
},
"example": {
"_links": {
"self": {
"href": "/api/v3/relations/1"
},
"update": {
"href": "/api/v3/relations/1/form",
"method": "POST"
},
"updateImmediately": {
"href": "/api/v3/relations/1",
"method": "PATCH"
},
"delete": {
"href": "/api/v3/relations/1",
"method": "DELETE"
},
"from": {
"href": "/api/v3/work_packages/42",
"title": "Steel Delivery"
},
"to": {
"href": "/api/v3/work_packages/84",
"title": "Bending the steel"
}
},
"_type": "Relation",
"id": 1,
"name": "precedes",
"type": "precedes",
"reverseType": "follows",
"description": "We can't bend the steel before it's been delivered!",
"lag": 0
}
}
400
Occurs when the client did not send a valid JSON object in the request body.
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:InvalidRequestBody",
"message": "The request body was not a single JSON object."
}
ErrorResponse
{
"type": "object",
"required": [
"_type",
"errorIdentifier",
"message"
],
"properties": {
"_embedded": {
"type": "object",
"properties": {
"details": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"example": "project"
}
}
}
}
},
"_type": {
"type": "string",
"enum": [
"Error"
]
},
"errorIdentifier": {
"type": "string",
"example": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation"
},
"message": {
"type": "string",
"example": "Project can't be blank."
}
}
}
404
Returned if the relation does not exist or the client does not have sufficient permissions to see it.
Required permission: view work packages
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:NotFound",
"message": "The specified relation does not exist."
}
ErrorResponse
{
"type": "object",
"required": [
"_type",
"errorIdentifier",
"message"
],
"properties": {
"_embedded": {
"type": "object",
"properties": {
"details": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"example": "project"
}
}
}
}
},
"_type": {
"type": "string",
"enum": [
"Error"
]
},
"errorIdentifier": {
"type": "string",
"example": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation"
},
"message": {
"type": "string",
"example": "Project can't be blank."
}
}
}
406
415
Occurs when the client sends an unsupported Content-Type header.
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:TypeNotSupported",
"message": "Expected CONTENT-TYPE to be (expected value) but got (actual value)."
}
ErrorResponse
{
"type": "object",
"required": [
"_type",
"errorIdentifier",
"message"
],
"properties": {
"_embedded": {
"type": "object",
"properties": {
"details": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"example": "project"
}
}
}
}
},
"_type": {
"type": "string",
"enum": [
"Error"
]
},
"errorIdentifier": {
"type": "string",
"example": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation"
},
"message": {
"type": "string",
"example": "Project can't be blank."
}
}
}
422
Returned if:
- the client tries to modify a read-only property (
PropertyIsReadOnly
) - a constraint for a property was violated (
PropertyConstraintViolation
) - the client provides a link to an invalid resource (
ResourceTypeMismatch
) or a work package that does not exist or for which the client does not have sufficient permissions to see it (required permissions:view work packages
for the involved work packages).
{
"_embedded": {
"details": {
"attribute": "lag"
}
},
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation",
"message": "Lag must be a number greater than or equal to 0"
}
ErrorResponse
{
"type": "object",
"required": [
"_type",
"errorIdentifier",
"message"
],
"properties": {
"_embedded": {
"type": "object",
"properties": {
"details": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"example": "project"
}
}
}
}
},
"_type": {
"type": "string",
"enum": [
"Error"
]
},
"errorIdentifier": {
"type": "string",
"example": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation"
},
"message": {
"type": "string",
"example": "Project can't be blank."
}
}
}
Delete relation
Deletes the relation.
id
integer
required path
The unique identifier of the relation resource
Example:1
204
Returned if the relation was deleted successfully. The response body is empty.
403
Returned if the client does not have sufficient permissions.
Required permission: manage work package relations
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:MissingPermission",
"message": "You are not allowed to delete this relation."
}
ErrorResponse
{
"type": "object",
"required": [
"_type",
"errorIdentifier",
"message"
],
"properties": {
"_embedded": {
"type": "object",
"properties": {
"details": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"example": "project"
}
}
}
}
},
"_type": {
"type": "string",
"enum": [
"Error"
]
},
"errorIdentifier": {
"type": "string",
"example": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation"
},
"message": {
"type": "string",
"example": "Project can't be blank."
}
}
}
404
Returned if the relation does not exist or the client does not have sufficient permissions to see it.
Required permission: view work packages
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:NotFound",
"message": "The specified relation does not exist."
}
ErrorResponse
{
"type": "object",
"required": [
"_type",
"errorIdentifier",
"message"
],
"properties": {
"_embedded": {
"type": "object",
"properties": {
"details": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"example": "project"
}
}
}
}
},
"_type": {
"type": "string",
"enum": [
"Error"
]
},
"errorIdentifier": {
"type": "string",
"example": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation"
},
"message": {
"type": "string",
"example": "Project can't be blank."
}
}
}
406
415
Occurs when the client sends an unsupported Content-Type header.
{
"_type": "Error",
"errorIdentifier": "urn:openproject-org:api:v3:errors:TypeNotSupported",
"message": "Expected CONTENT-TYPE to be (expected value) but got (actual value)."
}
ErrorResponse
{
"type": "object",
"required": [
"_type",
"errorIdentifier",
"message"
],
"properties": {
"_embedded": {
"type": "object",
"properties": {
"details": {
"type": "object",
"properties": {
"attribute": {
"type": "string",
"example": "project"
}
}
}
}
},
"_type": {
"type": "string",
"enum": [
"Error"
]
},
"errorIdentifier": {
"type": "string",
"example": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation"
},
"message": {
"type": "string",
"example": "Project can't be blank."
}
}
}