# OpenProject API v3 OpenProject is an open-source, web-based project management platform that empowers teams to plan, track, and collaborate on projects. It supports the full project lifecycle including task management, agile/scrum workflows, time tracking, cost reporting, meeting management, wiki pages, document storage, and GitHub integration. The platform is available as a self-hosted Community Edition or as an Enterprise cloud/on-premises offering. The codebase is a Ruby on Rails application with a modern TypeScript/Angular frontend and exposes a comprehensive REST API following the HAL+JSON specification. The core of OpenProject's programmatic interface is **APIv3**, a RESTful API built on the OpenAPI 3.1 specification, available at `/api/v3` on any OpenProject instance. The full machine-readable spec can be retrieved at `/api/v3/spec.json` or `/api/v3/spec.yml`. All responses use `application/hal+json` content type with hypermedia links (`_links`) for resource navigation. Authentication is performed via API key tokens passed as HTTP Basic Auth (with the username `apikey`) or via OAuth 2 client credentials. The API supports pagination (`offset`/`pageSize`), filtering via JSON-encoded filter arrays, and field selection via `select` parameters across nearly all collection endpoints. --- ## API Root — View Server Info Returns basic server instance information and a hypermedia map of all available top-level API resources. ```bash curl -u apikey:YOUR_API_KEY \ https://your-instance.openproject.com/api/v3 # Response: HAL+JSON object with _links to all resource collections ``` --- ## Work Packages — List Work Packages Returns a paginated collection of work packages across all visible projects. Supports rich filtering by status, assignee, type, date range, custom fields, and more. The `filters` parameter accepts a JSON array in the same format returned by the Queries endpoint. ```bash # List open work packages assigned to user 5, sorted by start date curl -u apikey:YOUR_API_KEY \ 'https://your-instance.openproject.com/api/v3/work_packages?filters=%5B%7B%22assigned_to%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%225%22%5D%7D%7D%2C%7B%22status%22%3A%7B%22operator%22%3A%22o%22%2C%22values%22%3Anull%7D%7D%5D&sortBy=%5B%5B%22start_date%22%2C%22asc%22%5D%5D&pageSize=25&offset=1' # Expected 200 response (truncated): # { # "_type": "Collection", # "total": 42, # "count": 25, # "_embedded": { # "elements": [ # { "_type": "WorkPackage", "id": 101, "subject": "Implement login", "startDate": "2024-03-01", ... } # ] # } # } ``` --- ## Work Packages — Create Work Package Creates a new work package. A `_links.project` reference is required. Start date, finish date, and duration are mutually validated — provide any two and the third is computed automatically. ```bash curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/work_packages \ -H 'Content-Type: application/json' \ -d '{ "subject": "Fix login bug", "startDate": "2024-04-01", "duration": "P3D", "_links": { "project": { "href": "/api/v3/projects/12" }, "type": { "href": "/api/v3/types/1" }, "status": { "href": "/api/v3/statuses/1" }, "assignee":{ "href": "/api/v3/users/5" } } }' # 200 Created — response includes computed dueDate: "2024-04-03" # 422 — { "_type": "Error", "errorIdentifier": "...PropertyConstraintViolation", "message": "Subject might not be blank." } ``` --- ## Work Packages — View / Update / Delete a Work Package Retrieve, patch, or delete a single work package by its integer ID. Updates use optimistic locking via `lockVersion` (required in PATCH body). Deleting a work package also removes all child work packages and associated time entries. ```bash # View curl -u apikey:YOUR_API_KEY \ https://your-instance.openproject.com/api/v3/work_packages/101 # Update (lockVersion must match current value from GET response) curl -u apikey:YOUR_API_KEY \ -X PATCH https://your-instance.openproject.com/api/v3/work_packages/101 \ -H 'Content-Type: application/json' \ -d '{ "lockVersion": 3, "subject": "Fix login bug (updated)", "percentageDone": 50, "_links": { "status": { "href": "/api/v3/statuses/3" } } }' # 409 conflict if lockVersion is stale: # { "_type": "Error", "errorIdentifier": "...UpdateConflict", "message": "Your changes could not be saved..." } # Delete curl -u apikey:YOUR_API_KEY \ -X DELETE https://your-instance.openproject.com/api/v3/work_packages/101 # 204 No Content on success ``` --- ## Projects — List Projects Returns all projects the current user can see. Supports filtering by `active`, `ancestor`, `favorited`, `principal`, `project_status_code`, `storage_id`, and more. Since OpenProject 17.0 the response also includes Programs and Portfolios. ```bash # List only active projects, selecting only identifier and name fields curl -u apikey:YOUR_API_KEY \ 'https://your-instance.openproject.com/api/v3/projects?filters=%5B%7B%22active%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%22t%22%5D%7D%7D%5D&select=total%2Celements%2Fidentifier%2Celements%2Fname&sortBy=%5B%5B%22name%22%2C%22asc%22%5D%5D' # 200 OK: # { # "_type": "Collection", # "total": 8, # "_embedded": { "elements": [ { "identifier": "my-project", "name": "My Project" }, ... ] } # } ``` --- ## Projects — Create Project Creates a new project at the top level or as a child of an existing project. ```bash curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/projects \ -H 'Content-Type: application/json' \ -d '{ "name": "Q3 Release", "identifier": "q3-release", "description": { "format": "markdown", "raw": "## Q3 Release Project\nTracking all Q3 deliverables." }, "public": false, "_links": { "parent": { "href": "/api/v3/projects/5" } } }' # 201 Created — full project object returned # 422 — { "message": "Name can't be blank.", "errorIdentifier": "...PropertyConstraintViolation" } ``` --- ## Users — List and Create Users Lists all users (requires admin or `manage_user` permission). Filterable by `status`, `group`, `name`, and `login`. Creating a user supports `"active"` status (password required) or `"invited"` status (email only, invitation sent automatically). ```bash # List users with status "invited" in group 3 curl -u apikey:YOUR_API_KEY \ 'https://your-instance.openproject.com/api/v3/users?filters=%5B%7B%22status%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%22invited%22%5D%7D%7D%2C%7B%22group%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%223%22%5D%7D%7D%5D' # Create an invited user curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/users \ -H 'Content-Type: application/json' \ -d '{ "login": "jane.doe", "firstName": "Jane", "lastName": "Doe", "email": "jane.doe@example.com", "status": "invited", "language": "en" }' # 201 Created — user object returned # 422 — { "message": "The email address is already taken." } ``` --- ## Memberships — List and Create Memberships Returns project memberships visible to the requesting user. Filterable by principal, project, role, status, and more. Creating a membership supports a custom notification message via `_meta.notificationMessage`, or suppressing notifications entirely with `_meta.sendNotifications: false`. ```bash # List all memberships for project 12 curl -u apikey:YOUR_API_KEY \ 'https://your-instance.openproject.com/api/v3/memberships?filters=%5B%7B%22project%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%2212%22%5D%7D%7D%5D' # Add user 8 to project 12 as Developer (role 4) with custom invite message curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/memberships \ -H 'Content-Type: application/json' \ -d '{ "_links": { "project": { "href": "/api/v3/projects/12" }, "principal": { "href": "/api/v3/users/8" }, "roles": [{ "href": "/api/v3/roles/4" }] }, "_meta": { "notificationMessage": { "format": "plain", "raw": "Welcome to the Q3 Release project!" } } }' # 201 Created — membership object returned ``` --- ## Time Entries — List and Create Time Entries Lists time entries visible to the current user. Filterable by `entity_type` (WorkPackage or Meeting), `entity_id`, `project_id`, `user_id`, `spent_on`, `ongoing`, and `activity_id`. Custom fields on time entries are also accepted when creating. ```bash # List time entries for work packages 1 and 2 in project 5, sorted by date curl -u apikey:YOUR_API_KEY \ 'https://your-instance.openproject.com/api/v3/time_entries?filters=%5B%7B%22entity_type%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%22WorkPackage%22%5D%7D%7D%2C%7B%22entity_id%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%221%22%2C%222%22%5D%7D%7D%5D&sortBy=%5B%5B%22spent_on%22%2C%22asc%22%5D%5D' # Log 2.5 hours on work package 42 curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/time_entries \ -H 'Content-Type: application/json' \ -d '{ "hours": "PT2H30M", "spentOn": "2024-04-15", "comment": { "format": "plain", "raw": "Reviewed PR and wrote tests" }, "_links": { "workPackage": { "href": "/api/v3/work_packages/42" }, "activity": { "href": "/api/v3/time_entries/activities/1" } } }' # 201 Created # 422 — { "message": "Work package is invalid." } ``` --- ## Versions — List and Create Versions Returns project versions (milestones/releases). Filterable by `sharing` scope (`none`, `descendants`, `hierarchy`, `tree`, `system`) and `name`. Versions can be shared across the project hierarchy. ```bash # List all system-shared versions curl -u apikey:YOUR_API_KEY \ 'https://your-instance.openproject.com/api/v3/versions?filters=%5B%7B%22sharing%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%22system%22%5D%7D%7D%5D' # Create a version in project 12 curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/versions \ -H 'Content-Type: application/json' \ -d '{ "name": "v2.0.0", "description": { "format": "plain", "raw": "Major release" }, "startDate": "2024-05-01", "endDate": "2024-06-30", "status": "open", "sharing": "descendants", "_links": { "definingProject": { "href": "/api/v3/projects/12" } } }' # 201 Created ``` --- ## Relations — List Work Package Relations Lists all relations between work packages (follows, precedes, blocks, relates, duplicates, etc.). Filterable by `from`, `to`, `involved`, and `type`. Only relations between visible work packages are returned. ```bash # List all relations involving work package 42 curl -u apikey:YOUR_API_KEY \ 'https://your-instance.openproject.com/api/v3/relations?filters=%5B%7B%22involved%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%2242%22%5D%7D%7D%5D&sortBy=%5B%5B%22type%22%2C%22asc%22%5D%5D' # Create a "follows" relation: WP 43 follows WP 42 (with 2-day lag) curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/work_packages/43/relations \ -H 'Content-Type: application/json' \ -d '{ "type": "follows", "lag": 2, "_links": { "to": { "href": "/api/v3/work_packages/42" } } }' # 201 Created ``` --- ## Queries — List and Create Saved Queries Queries are saved filters/sort configurations for work packages. They can be global or project-scoped. Filterable by `project` and `id`. Creating a query persists the filter/sort/column configuration for reuse. ```bash # List all global queries (not tied to a project) curl -u apikey:YOUR_API_KEY \ 'https://your-instance.openproject.com/api/v3/queries?filters=%5B%7B%22project%22%3A%7B%22operator%22%3A%22!*%22%2C%22values%22%3Anull%7D%7D%5D' # Create a project-scoped query showing only "In Progress" tasks sorted by due date curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/queries \ -H 'Content-Type: application/json' \ -d '{ "name": "In Progress by Due Date", "public": true, "filters": [{ "status": { "operator": "=", "values": ["3"] } }], "sortBy": [["due_date", "asc"]], "columns": ["id", "subject", "assignee", "dueDate"], "_links": { "project": { "href": "/api/v3/projects/12" } } }' # 201 Created ``` --- ## Notifications — List In-App Notifications Returns in-app notifications for the authenticated user. Supports filtering by `readIAN`, `reason`, `project`, `resourceType`, and `resourceId`. Groupable by `reason` or `project`. ```bash # List all unread notifications grouped by reason curl -u apikey:YOUR_API_KEY \ 'https://your-instance.openproject.com/api/v3/notifications?filters=%5B%7B%22readIAN%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%22f%22%5D%7D%7D%5D&groupBy=reason&pageSize=20' # Mark all notifications as read curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/notifications/read_ian \ -H 'Content-Type: application/json' \ -d '{}' ``` --- ## Meetings — List and Create Meetings Retrieve visible meetings or create a new one-time or recurring meeting. Meetings can have agenda items, sections, and attached files. ```bash # List all visible meetings curl -u apikey:YOUR_API_KEY \ https://your-instance.openproject.com/api/v3/meetings # Create a meeting in project 12 curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/meetings \ -H 'Content-Type: application/json' \ -d '{ "title": "Sprint Review — Week 16", "startTime": "2024-04-19T10:00:00Z", "duration": "PT1H", "location": "Conference Room B", "_links": { "project": { "href": "/api/v3/projects/12" } } }' # 201 Created ``` --- ## Attachments — Create Attachment Uploads a file as a standalone attachment (without a container), which can later be claimed by a work package, wiki page, or meeting. The request must be `multipart/form-data` with a `metadata` JSON part and a `file` binary part. ```bash curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/attachments \ -F 'metadata={"fileName":"architecture.png","description":{"raw":"System architecture diagram"}};type=application/json' \ -F 'file=@/path/to/architecture.png;type=image/png' # 200 OK — attachment object returned with _links.self for later claiming # 422 — { "message": "File is too large (maximum size is 5242880 Bytes)." } # Attach directly to a work package curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/work_packages/42/attachments \ -F 'metadata={"fileName":"spec.pdf"};type=application/json' \ -F 'file=@/path/to/spec.pdf;type=application/pdf' ``` --- ## Wiki Pages — View Wiki Page Retrieves an individual wiki page by its integer ID. The response includes HAL links to the owning project and attachment endpoints. ```bash curl -u apikey:YOUR_API_KEY \ https://your-instance.openproject.com/api/v3/wiki_pages/72 # 200 OK: # { # "_type": "WikiPage", # "id": 72, # "title": "A wiki page with a name", # "_links": { # "self": { "href": "/api/v3/wiki_pages/72" }, # "project": { "href": "/api/v3/projects/12", "title": "some project" }, # "attachments": { "href": "/api/v3/wiki_pages/72/attachments" }, # "addAttachment": { "href": "/api/v3/wiki_pages/72/attachments", "method": "post" } # } # } ``` --- ## Storages — List and Create File Storages Manages external file storage integrations (e.g., Nextcloud, OneDrive). Creating a storage automatically provisions an OAuth 2 provider application — the OAuth client secret is only visible in the creation response. ```bash # List configured storages curl -u apikey:YOUR_API_KEY \ https://your-instance.openproject.com/api/v3/storages # Create a Nextcloud storage integration curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/storages \ -H 'Content-Type: application/json' \ -d '{ "name": "Team Nextcloud", "host": "https://nextcloud.example.com", "_links": { "type": { "href": "/api/v3/storage_types/nextcloud" } } }' # 201 Created — includes oauthClientId and oauthClientSecret (shown only once) ``` --- ## Grids — List and Create Dashboard Grids Grids represent configurable widget layouts used for My Page and project overview dashboards. Each grid is tied to a page URL and contains an array of positioned widget definitions. ```bash # List grids for the "My Page" dashboard curl -u apikey:YOUR_API_KEY \ 'https://your-instance.openproject.com/api/v3/grids?filters=%5B%7B%22page%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%22%2Fmy%2Fpage%22%5D%7D%7D%5D' # Create a grid with two widgets curl -u apikey:YOUR_API_KEY \ -X POST https://your-instance.openproject.com/api/v3/grids \ -H 'Content-Type: application/json' \ -d '{ "rowCount": 4, "columnCount": 2, "widgets": [ { "identifier": "work_packages_assigned", "startRow": 1, "endRow": 3, "startColumn": 1, "endColumn": 2 }, { "identifier": "calendar", "startRow": 3, "endRow": 5, "startColumn": 1, "endColumn": 3 } ], "_links": { "scope": { "href": "/my/page" } } }' # 201 Created ``` --- ## Capabilities — List User Permissions Returns the actions a principal is allowed to perform in a given context (project/workspace or global). Filterable by `action`, `principal`, and `context`. Use `w{id}` for workspace/project contexts and `g` for the global context. ```bash # List all capabilities of user 567 in project/workspace 123 curl -u apikey:YOUR_API_KEY \ 'https://your-instance.openproject.com/api/v3/capabilities?filters=%5B%7B%22principal%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%22567%22%5D%7D%7D%2C%7B%22context%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%22w123%22%5D%7D%7D%5D' # 200 OK (excerpt): # { "_embedded": { "elements": [ # { "id": "work_packages/create/w123-567", "_type": "Capability", # "_links": { "action": { "href": "/api/v3/actions/work_packages/create" }, # "context": { "href": "/api/v3/projects/123" }, # "principal": { "href": "/api/v3/users/567" } } } # ]}} ``` --- ## Markdown Rendering — Preview Markdown Renders a Markdown string to HTML, optionally within the context of a specific work package (enabling context-aware extensions like embedded attachment images). ```bash curl -u apikey:YOUR_API_KEY \ -X POST 'https://your-instance.openproject.com/api/v3/render/markdown?context=/api/v3/work_packages/42' \ -H 'Content-Type: text/plain' \ -d '## Summary This fixes the issue described in #42. See attached image: ' # 200 OK — Content-Type: text/html #
This fixes the issue described in #42. See attached image: