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 the to 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

Occurs when the client did not send a Content-Type header

"Missing content-type header"
{
  "type": "string"
}

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

Occurs when the client did not send a Content-Type header

"Missing content-type header"
{
  "type": "string"
}

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."
    }
  }
}