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  |
                      | delay        |
                      +--------------+

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
delay* The delay in 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

{
  "_embedded": {
    "elements": [
      {
        "_links": {
          "delete": {
            "href": "/api/v3/relations/1",
            "method": "DELETE"
          },
          "from": {
            "href": "/api/v3/work_packages/42",
            "title": "Steel Delivery"
          },
          "self": {
            "href": "/api/v3/relations/1"
          },
          "to": {
            "href": "/api/v3/work_packages/84",
            "title": "Bending the steel"
          },
          "update": {
            "href": "/api/v3/relations/1/form",
            "method": "POST"
          },
          "updateImmediately": {
            "href": "/api/v3/relations/1",
            "method": "PATCH"
          }
        },
        "_type": "Relation",
        "delay": 0,
        "description": "We can't bend the steel before it's been delivered!",
        "id": 1,
        "name": "precedes",
        "reverseType": "follows",
        "type": "precedes"
      }
    ]
  },
  "_links": {
    "self": {
      "href": "/api/v3/relations"
    }
  },
  "_type": "Collection",
  "count": 1,
  "total": 3
}

RelationsModel

{
  "type": "object",
  "example": {
    "_links": {
      "self": {
        "href": "/api/v3/relations"
      }
    },
    "total": 3,
    "count": 1,
    "_type": "Collection",
    "_embedded": {
      "elements": [
        {
          "_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!",
          "delay": 0
        }
      ]
    }
  }
}

View relation schema

No parameters

200

OK

{
  "_links": {
    "self": {
      "href": "/api/v3/relations/schema"
    }
  },
  "_type": "Schema",
  "delay": {
    "name": "Delay",
    "type": "Integer",
    "writable": true
  },
  "description": {
    "name": "Description",
    "type": "String",
    "writable": true
  },
  "from": {
    "name": "From work package",
    "type": "WorkPackage",
    "writable": false
  },
  "id": {
    "name": "ID",
    "type": "Integer",
    "writable": false
  },
  "reverseType": {
    "name": "Reverse Type",
    "type": "String",
    "writable": false
  },
  "to": {
    "name": "To work package",
    "type": "WorkPackage",
    "writable": false
  },
  "type": {
    "name": "Type",
    "type": "String",
    "writable": true
  }
}

Relation_schemaModel

{
  "type": "object",
  "example": {
    "_type": "Schema",
    "_links": {
      "self": {
        "href": "/api/v3/relations/schema"
      }
    },
    "id": {
      "name": "ID",
      "type": "Integer",
      "writable": false
    },
    "type": {
      "name": "Type",
      "type": "String",
      "writable": true
    },
    "reverseType": {
      "name": "Reverse Type",
      "type": "String",
      "writable": false
    },
    "description": {
      "name": "Description",
      "type": "String",
      "writable": true
    },
    "from": {
      "name": "From work package",
      "type": "WorkPackage",
      "writable": false
    },
    "to": {
      "name": "To work package",
      "type": "WorkPackage",
      "writable": false
    },
    "delay": {
      "name": "Delay",
      "type": "Integer",
      "writable": true
    }
  }
}

View relation schema for type

type
string

required path

Type of the schema

Example:
follows

200

OK

{
  "_links": {
    "self": {
      "href": "/api/v3/relations/schema"
    }
  },
  "_type": "Schema",
  "delay": {
    "name": "Delay",
    "type": "Integer",
    "writable": true
  },
  "description": {
    "name": "Description",
    "type": "String",
    "writable": true
  },
  "from": {
    "name": "From work package",
    "type": "WorkPackage",
    "writable": false
  },
  "id": {
    "name": "ID",
    "type": "Integer",
    "writable": false
  },
  "reverseType": {
    "name": "Reverse Type",
    "type": "String",
    "writable": false
  },
  "to": {
    "name": "To work package",
    "type": "WorkPackage",
    "writable": false
  },
  "type": {
    "name": "Type",
    "type": "String",
    "writable": true
  }
}

Relation_schemaModel

{
  "type": "object",
  "example": {
    "_type": "Schema",
    "_links": {
      "self": {
        "href": "/api/v3/relations/schema"
      }
    },
    "id": {
      "name": "ID",
      "type": "Integer",
      "writable": false
    },
    "type": {
      "name": "Type",
      "type": "String",
      "writable": true
    },
    "reverseType": {
      "name": "Reverse Type",
      "type": "String",
      "writable": false
    },
    "description": {
      "name": "Description",
      "type": "String",
      "writable": true
    },
    "from": {
      "name": "From work package",
      "type": "WorkPackage",
      "writable": false
    },
    "to": {
      "name": "To work package",
      "type": "WorkPackage",
      "writable": false
    },
    "delay": {
      "name": "Delay",
      "type": "Integer",
      "writable": true
    }
  }
}

404

Returned if the relation type does not exist or the client does not have sufficient permissions to see it.

Required permission: manage work package relations

{
  "_type": "Error",
  "errorIdentifier": "urn:openproject-org:api:v3:errors:NotFound",
  "message": "The specified relation type does not exist."
}

View relation

id
integer

required path

Relation id

Example:
1

200

OK

{
  "_links": {
    "delete": {
      "href": "/api/v3/relations/1",
      "method": "DELETE"
    },
    "from": {
      "href": "/api/v3/work_packages/42",
      "title": "Steel Delivery"
    },
    "self": {
      "href": "/api/v3/relations/1"
    },
    "to": {
      "href": "/api/v3/work_packages/84",
      "title": "Bending the steel"
    },
    "update": {
      "href": "/api/v3/relations/1/form",
      "method": "POST"
    },
    "updateImmediately": {
      "href": "/api/v3/relations/1",
      "method": "PATCH"
    }
  },
  "_type": "Relation",
  "delay": 0,
  "description": "We can't bend the steel before it's been delivered!",
  "id": 1,
  "name": "precedes",
  "reverseType": "follows",
  "type": "precedes"
}

RelationModel

{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "description": "Relation ID",
      "readOnly": true,
      "minimum": 0,
      "exclusiveMinimum": true
    },
    "name": {
      "type": "string",
      "description": "The internationalized name of this kind of relation"
    },
    "type": {
      "type": "string",
      "description": "Which kind of relation (blocks, precedes, etc.)"
    },
    "reverseType": {
      "type": "string",
      "description": "The kind of relation from the other WP's perspective",
      "readOnly": true
    },
    "description": {
      "type": "string",
      "description": "Short text further describing the relation"
    },
    "delay*": {
      "type": "integer",
      "description": "The delay in days between closing of `from` and start of `to`",
      "minimum": 0
    },
    "_links": {
      "type": "object",
      "required": [
        "self",
        "schema",
        "from",
        "to"
      ],
      "properties": {
        "update": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Link"
            },
            {
              "description": "Updates the relation between two work packages via a form\n\n# Conditions\n\n**Permission**: manage work package relations",
              "readOnly": true
            }
          ]
        },
        "updateImmediately": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Link"
            },
            {
              "description": "Updates the relation between two work packages\n\n# Conditions\n\n**Permission**: manage work package relations",
              "readOnly": true
            }
          ]
        },
        "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",
              "readOnly": true
            }
          ]
        },
        "self": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Link"
            },
            {
              "description": "This relation\n\n**Resource**: Relation\n\n# Conditions\n\n**Permission**: view work packages",
              "readOnly": true
            }
          ]
        },
        "schema": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Link"
            },
            {
              "description": "The schema of this relation\n\n**Resource**: Schema",
              "readOnly": true
            }
          ]
        },
        "from": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Link"
            },
            {
              "description": "The emanating work package\n\n**Resource**: WorkPackage\n\n# Conditions\n\n**Permission**: view work packages",
              "readOnly": true
            }
          ]
        },
        "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",
              "readOnly": true
            }
          ]
        }
      }
    }
  },
  "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!",
    "delay": 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."
}

Edit 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

{
  "_links": {
    "delete": {
      "href": "/api/v3/relations/1",
      "method": "DELETE"
    },
    "from": {
      "href": "/api/v3/work_packages/42",
      "title": "Steel Delivery"
    },
    "self": {
      "href": "/api/v3/relations/1"
    },
    "to": {
      "href": "/api/v3/work_packages/84",
      "title": "Bending the steel"
    },
    "update": {
      "href": "/api/v3/relations/1/form",
      "method": "POST"
    },
    "updateImmediately": {
      "href": "/api/v3/relations/1",
      "method": "PATCH"
    }
  },
  "_type": "Relation",
  "delay": 0,
  "description": "We can't bend the steel before it's been delivered!",
  "id": 1,
  "name": "precedes",
  "reverseType": "follows",
  "type": "precedes"
}

RelationModel

{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "description": "Relation ID",
      "readOnly": true,
      "minimum": 0,
      "exclusiveMinimum": true
    },
    "name": {
      "type": "string",
      "description": "The internationalized name of this kind of relation"
    },
    "type": {
      "type": "string",
      "description": "Which kind of relation (blocks, precedes, etc.)"
    },
    "reverseType": {
      "type": "string",
      "description": "The kind of relation from the other WP's perspective",
      "readOnly": true
    },
    "description": {
      "type": "string",
      "description": "Short text further describing the relation"
    },
    "delay*": {
      "type": "integer",
      "description": "The delay in days between closing of `from` and start of `to`",
      "minimum": 0
    },
    "_links": {
      "type": "object",
      "required": [
        "self",
        "schema",
        "from",
        "to"
      ],
      "properties": {
        "update": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Link"
            },
            {
              "description": "Updates the relation between two work packages via a form\n\n# Conditions\n\n**Permission**: manage work package relations",
              "readOnly": true
            }
          ]
        },
        "updateImmediately": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Link"
            },
            {
              "description": "Updates the relation between two work packages\n\n# Conditions\n\n**Permission**: manage work package relations",
              "readOnly": true
            }
          ]
        },
        "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",
              "readOnly": true
            }
          ]
        },
        "self": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Link"
            },
            {
              "description": "This relation\n\n**Resource**: Relation\n\n# Conditions\n\n**Permission**: view work packages",
              "readOnly": true
            }
          ]
        },
        "schema": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Link"
            },
            {
              "description": "The schema of this relation\n\n**Resource**: Schema",
              "readOnly": true
            }
          ]
        },
        "from": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Link"
            },
            {
              "description": "The emanating work package\n\n**Resource**: WorkPackage\n\n# Conditions\n\n**Permission**: view work packages",
              "readOnly": true
            }
          ]
        },
        "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",
              "readOnly": true
            }
          ]
        }
      }
    }
  },
  "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!",
    "delay": 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."
}

404

Returned if the relation does not exist or the client does not have sufficient permissions to see it.

Required permission: manage work package relations

{
  "_type": "Error",
  "errorIdentifier": "urn:openproject-org:api:v3:errors:NotFound",
  "message": "The specified relation does not exist."
}

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": "delay"
    }
  },
  "_type": "Error",
  "errorIdentifier": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation",
  "message": "Delay must be a number greater than or equal to 0"
}

Delete relation

Deletes the relation.

id
integer

required path

Relation ID

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

404

Returned if the relation does not exist or the client does not have sufficient permissions to see it.

Required permission: manage work package relations

{
  "_type": "Error",
  "errorIdentifier": "urn:openproject-org:api:v3:errors:NotFound",
  "message": "The specified relation does not exist."
}

Relation edit form

id
integer

required path

ID of the relation being modified

Example:
1

200

OK

{
  "_embedded": {
    "payload": {
      "_links": {
        "from": {
          "href": "/api/v3/work_packages/4534"
        },
        "to": {
          "href": "/api/v3/work_packages/3857"
        }
      },
      "_type": "WorkPackage",
      "delay": 3,
      "description": "let it rest for 3 days",
      "type": "follows"
    },
    "schema": {
      "_links": {
        "self": {
          "href": "/api/v3/relations/schema"
        }
      },
      "_type": "Schema",
      "delay": {
        "name": "Delay",
        "type": "Integer",
        "writable": true
      },
      "description": {
        "name": "Description",
        "type": "String",
        "writable": true
      },
      "from": {
        "name": "From work package",
        "type": "WorkPackage",
        "writeable": false
      },
      "id": {
        "name": "ID",
        "type": "Integer",
        "writable": false
      },
      "reverseType": {
        "name": "Reverse Type",
        "type": "String",
        "writable": false
      },
      "to": {
        "name": "To work package",
        "type": "WorkPackage",
        "writable": false
      },
      "type": {
        "allowedValues": [
          "relates",
          "duplicates",
          "duplicated",
          "blocks",
          "blocked",
          "precedes",
          "follows",
          "includes",
          "partof",
          "requires",
          "required"
        ],
        "name": "Type",
        "type": "String",
        "writable": true
      }
    },
    "validationErrors": {
      "from": {
        "_type": "Error",
        "errorIdentifier": "urn:openproject-org:api:v3:errors:BadExampleError",
        "message": "For the purpose of this example we need a validation error. The remainder of the response pretends there were no errors."
      }
    }
  },
  "_links": {
    "commit": {
      "href": "/api/v3/relations",
      "method": "PATCH"
    },
    "self": {
      "href": "/api/v3/relations/form"
    },
    "validate": {
      "href": "/api/v3/relations/form",
      "method": "POST"
    }
  },
  "_type": "Form"
}

Relation_edit_formModel

{
  "type": "object",
  "example": {
    "_links": {
      "self": {
        "href": "/api/v3/relations/form"
      },
      "validate": {
        "href": "/api/v3/relations/form",
        "method": "POST"
      },
      "commit": {
        "href": "/api/v3/relations",
        "method": "PATCH"
      }
    },
    "_type": "Form",
    "_embedded": {
      "payload": {
        "_links": {
          "from": {
            "href": "/api/v3/work_packages/4534"
          },
          "to": {
            "href": "/api/v3/work_packages/3857"
          }
        },
        "_type": "WorkPackage",
        "type": "follows",
        "delay": 3,
        "description": "let it rest for 3 days"
      },
      "schema": {
        "_type": "Schema",
        "_links": {
          "self": {
            "href": "/api/v3/relations/schema"
          }
        },
        "id": {
          "name": "ID",
          "type": "Integer",
          "writable": false
        },
        "type": {
          "name": "Type",
          "type": "String",
          "writable": true,
          "allowedValues": [
            "relates",
            "duplicates",
            "duplicated",
            "blocks",
            "blocked",
            "precedes",
            "follows",
            "includes",
            "partof",
            "requires",
            "required"
          ]
        },
        "reverseType": {
          "name": "Reverse Type",
          "type": "String",
          "writable": false
        },
        "description": {
          "name": "Description",
          "type": "String",
          "writable": true
        },
        "from": {
          "name": "From work package",
          "type": "WorkPackage",
          "writeable": false
        },
        "to": {
          "name": "To work package",
          "type": "WorkPackage",
          "writable": false
        },
        "delay": {
          "name": "Delay",
          "type": "Integer",
          "writable": true
        }
      },
      "validationErrors": {
        "from": {
          "_type": "Error",
          "errorIdentifier": "urn:openproject-org:api:v3:errors:BadExampleError",
          "message": "For the purpose of this example we need a validation error. The remainder of the response pretends there were no errors."
        }
      }
    }
  }
}

403

Returned if the client does not have sufficient permissions.

Required permission: manage work package relations

Note that you will only receive this error, if you are at least allowed to see the involved work packages.

{
  "_type": "Error",
  "errorIdentifier": "urn:openproject-org:api:v3:errors:MissingPermission",
  "message": "You are not allowed to edit the specified relation."
}

404

Returned if the relation does not exist or the client does not have sufficient permissions to see it.

Required permission: view (involved) work package(s), manage work package relations

{
  "_type": "Error",
  "errorIdentifier": "urn:openproject-org:api:v3:errors:NotFound",
  "message": "The specified relation does not exist."
}