Umstieg von API V2 auf V3

Was sich mit API V3 ändert und wie Sie Ihre Integrationen und Automationen umstellen. API V2 bleibt bis Ende 2028 verfügbar.

PQFORCE 4.26.6 Preview

Diese Dokumentation gibt es nur auf Englisch.

Vorschau: Diese Dokumentation beschreibt PQFORCE 4.26.6, das noch nicht veröffentlicht ist.

Audience — developers who consume the PQFORCE API today and want to move to generation V3:

  • Client developers building a user interface or a desktop/mobile app on the REST API.
  • JavaScript developers writing server-side automations (Pqf.* inside a PQFORCE JavaScript source).
  • API / integration developers connecting a third-party system through the public API and a bearer token.

Scope — this guide covers only endpoints that are publicly available: the REST API (client and public API) and the JavaScript API. Endpoints reserved for the PQFORCE browser client are out of scope and are not listed. V2 endpoints that were already maintenance-only are included, because tooling built on them needs a successor too; their tables say so, and they need maintenance authentication rather than a bearer token.

Status — API V3 is complete. As of PQFORCE 4.26.6, every publicly available V2 endpoint either has a V3 successor or is retired; none is waiting for migration. This guide describes that state.

Reference documentation — this guide tells you what to change. The exact syntax, parameters, permissions and data contracts of every V3 endpoint are in the API V3 reference documentation and in the machine-readable definitions described in section 3.


1. Why migrate, and how much time you have

API V3 is the generation that all new PQFORCE functionality targets. It is not a re-packaging of V2: it introduces sorting, bulk reads, object identifications, version timelines, a waste bin, and a consistent naming and permission model. Capabilities added from now on appear in V3 only.

API V2 remains available and supported. Every deprecated V2 endpoint carries a note naming its successor and the sentence “will be discontinued at end of 2028”. Treat that as the planning horizon: V2 is switched off at the end of 2028, and everything that only works because a V2 compatibility rule exists stops working with it.

At 4.26.6, 100 % of the relevant V2 surface has a V3 successor — every endpoint that is not retired. A full migration is therefore possible for every module; section 2.5 has the per-module picture. You do not have to move everything at once: V2 and V3 can be called side by side, in the same integration and even in the same JavaScript automation, so migrating module by module is expected and fully supported.


2. How a V2 endpoint reaches V3

Every publicly available V2 endpoint is in exactly one of three states: redirected, reimplemented or retired. The fourth state, not yet in V3, is empty.

2.1 Redirected — 205 endpoints

The V2 endpoint has a V3 successor with an identical signature: same parameters, same request body, same response body. Calling the V2 URL answers 307 Temporary Redirect with the V3 URL in the Location header.

  • REST: your HTTP client must follow redirects. Note that 307 (unlike 301/302) preserves the method and the body, so a redirected PUT or POST is re-sent unchanged — but only if your client library is configured to follow redirects for those methods. Many are not by default.
  • Once redirected, you are talking to V3. The response you get is a genuine V3 response. Where V2 and V3 disagree on a value — most notably the work-item object type, see section 6 — you receive the V3 spelling.
  • JavaScript: there is no HTTP layer, so there is no redirect. The V2 function calls the V3 implementation directly and is marked deprecated. Behaviour is already V3 behaviour.
  • What to do: switch the URL (or the function name) at your convenience. Nothing else changes. Do it before the end of 2028.

2.2 Reimplemented — 521 endpoints

The V2 endpoint has a V3 successor, but the call differs: a renamed endpoint, a different parameter list, a different request or response body, or a combination. There is no redirect — the V2 URL keeps serving the V2 behaviour until V2 is switched off.

What to do: rewrite the call. Section 10 names the successor and says what differs; the reference documentation has the details.

2.3 Retired — 51 endpoints

No V3 successor, by design: the functionality moved elsewhere, was replaced by a general mechanism, or is discontinued. These endpoints disappear when V2 does. Section 8 lists the whole-module cases; section 10 lists them per module with the advice from their deprecation note. One of them, PM getProjectScenarioChangesTimeline, is retired only for now: its V3 successor follows with the Allocation 2.0 feature.

2.4 Not yet in V3 — none

No V2 endpoint is waiting for migration any more. The migration is complete.

2.5 Per-module readiness

Module Scope Redirected Reimplemented Retired Not yet in V3
ACM Access control, users, tokens, single sign-on 17 41 2 0
ACT Actual times and presences 8 19 7 0
ALC Allocations and capacity planning 3 30 1 0
ATM Automation (JavaScript sources and triggers) 15 5 0 0
CHT Charts 0 2 0 0
CLF Client framework (cockpit widgets, stores, translations) 18 6 1 0
CLS Classification schemes 1 4 0 0
DOC Legacy document exports 0 0 8 0
DPO Disposition views 1 0 8 0
EXP Exports 3 4 0 0
FCO Financial controlling (costs, budgets, currencies) 14 63 0 0
LCY Lifecycles 7 27 0 0
LIC Licensing 0 5 0 0
MAIN Server information and session start 3 3 0 0
MSG Messages, forwarding rules and mail 7 9 0 0
MTG Meetings 8 9 1 0
NAV Navigation and search 0 2 8 0
PF Platform (relations, attachments, properties, enums, icons) 29 53 8 0
PI Project items 3 7 0 0
PM Project management (projects, portfolios, reports, scenarios, work items) 43 133 3 0
RES Resources, resource links, holidays and availabilities 11 46 4 0
RSK Risks 1 14 0 0
STG Strategy 12 30 0 0
TDP External (third-party) object references 1 9 0 0
Total 205 521 51 0

Fully migrated: ACM, ACT, ALC, ATM, CHT, CLF, CLS, EXP, FCO, LCY, LIC, MAIN, MSG, MTG, PF, PI, PM, RES, RSK, STG, TDP. Retired outright: DOC, DPO, NAV.


3. New machine-readable definitions

Do not port a generated client by hand. Regenerate it from the V3 definitions.

3.1 OpenAPI

V2 V3
Standard OpenAPI 3.0.3 OpenAPI 3.2.0
JSON GET /API/V2/MAIN/ApiDescription/OpenApi/303/Json GET /API/V3/MAIN/ApiDescription/OpenApi/Json
YAML GET /API/V2/MAIN/ApiDescription/OpenApi/303/Yaml GET /API/V3/MAIN/ApiDescription/OpenApi/Yaml

Note that the V3 URL carries no standard-version segment — there is no /320/ in it.

Both accept a modules filter to keep the file small enough for tooling that struggles with the full description: ?modules=pm, or ?modules="pm","fco" for several. Replace the host with your own instance's host.

Only endpoints reachable from the public API appear in the OpenAPI description. Endpoints available exclusively inside the JavaScript sandbox are documented in the TypeScript definition instead.

3.2 TypeScript definitions — new in V3

GET /API/V3/MAIN/ApiDescription/TypeScript

There is no V2 counterpart: this is new. It declares the whole JavaScript surface — Pqf, PqfV2 and PqfV3 namespaces, one function per endpoint (with one overload per omitted trailing optional parameter), and every data contract as an interface. Contract inheritance is preserved as extends, enum contracts become string enums, and every nullable member is typed T | null.

Point your editor at it and you get completion and type checking while writing a PQFORCE automation. It requires the JavaScript licence feature, like the automations themselves.

3.3 Assertions are not in the schema

Many V3 parameters and body members carry an assertion — a length, a range, a regular expression, an array bound. Assertions are documentation: they are deliberately not expressed as constraints in the OpenAPI schema or in the generated TypeScript types, because they would not survive most code generators intact. Validate against them yourself; a violation is answered with 422 before the request is processed.


4. What does not change

Do not spend migration effort on these — they are identical in V3.

  • The four access kinds and their authentication: session cookie (plus Csrf-Token for PUT/POST/DELETE) for the client API, Authorization: Bearer for the public API, certificate and challenge headers for the maintenance API, and the in-process JavaScript API.
  • Tokens. An API token created and authorized for V2 works for V3. There is no separate V3 token.
  • The tenant alias and the way it enters the URL.
  • Accept-Language for language and X-Timezone for time zone.
  • pf.Reference — still { type, id }, with the same meaning.
  • Array parameter encoding — a JSON array without the enclosing brackets. One value may be written bare (sort=name), several must be quoted and comma-separated (sort="name:desc","code"). V3 additionally accepts the fully bracketed form (sort=["name:desc","code"]).
  • HTML sanitizing. Any string containing <, > or & is treated as HTML and must be HTML-encoded on the way in and decoded on the way out, unless the member is explicitly marked as not sanitized.
  • The error-code concept. Responses carry coded references, not messages; the four-character code identifies the log entry.

5. Concepts that changed

5.1 Endpoint naming and URL shape

V3 applies one naming scheme across all modules, so an endpoint you have never seen is usually guessable.

Purpose Name URL
Read one object get<Object> GET /API/V3/<MOD>/<Object>/:id
Read several objects get<Objects> GET /API/V3/<MOD>/<Objects>/:ids
Read all objects (as references) getAll<Objects> GET /API/V3/<MOD>/<Objects>
Read a filtered set get<Objects>By<Discriminator> GET /API/V3/<MOD>/…
Create or update one object put<Object> PUT /API/V3/<MOD>/<Object>/:id
Create with a new ID add<Object> POST /API/V3/<MOD>/…/Add
Delete del<Object> DELETE /API/V3/<MOD>/<Object>/:id
Display data only get<Object>Identification(s) GET /API/V3/<MOD>/<Object(s)>/:id(s)/Identification
Version history get<Object>Timeline, get<Object>Version …/:id/Timeline, …/:id/Version/:versionId
Waste bin getRecentlyDeleted<Objects>, undel<Object> …/<Objects>/Deleted/Recently, …/:id/Undelete

Two consequences for readers of V2 code:

  • The plural URL segment means "several by ID", not "all". V2's GET /API/V2/PM/Projects returned every project; V3's GET /API/V3/PM/Projects/:projectIds returns the ones you name, and GET /API/V3/PM/Projects returns references to all of them (section 5.2).
  • A sub-collection is named after the relationship, with /Add appended for the adder — for example GET …/SubWorkItems and POST …/SubWorkItems/Add.

The same name can mean something different. Because V3 reserves the plural for "several by ID", a number of V2 names survive into V3 with a different meaning: pm.getProjects, acm.getUsers, mtg.getMeetings, stg.getStrategies, pi.getProjectItemTypes and others returned everything in V2 and now return the ones you name. A call that compiles and returns 200 may therefore return the wrong thing. Check every plural getter against section 10 rather than assuming the name carried its meaning across.

Where a module's V2 vocabulary was inconsistent, V3 renamed it. The largest case is the external-reference module (TDP), where four V2 concepts collapse into two; see section 10.

5.2 References first, details second — and how you page

This is the single most important change for anything that shows a list.

V2 offered unbounded "give me everything, in full" endpoints. GET /API/V2/PM/Projects returned a complete Project object for every project the user may see. That response grows with the tenant, cannot be ordered, and cannot be split.

V3 has no such endpoint. The pattern is two steps:

  1. getAll<Objects> returns a cheap, orderable list of references. Of the 45 getAll… endpoints, 20 return a plain pf.Reference (type + id), 14 return a reference plus its parent (pf.ReferenceWithParent) or its parent chain (pf.ReferenceWithParents), and the remainder return a small module-specific reference type. The list is filtered by permission — you only see what you are allowed to see.
  2. get<Objects>/:ids reads the details for a slice of that list, up to pqforce.performance.bulk.max IDs per call — a configured limit whose default is 100. Read the value your instance uses rather than hard-coding it (section 5.13).

That is the paging mechanism. There is no offset/limit on the detail read: you own the reference list, so you decide which slice to fetch, in which order, and how far to scroll.

GET /API/V3/PM/Projects?sort="name"
    -> [ {type:"Project", id:"…A"}, {type:"Project", id:"…B"}, … ]     (all, ordered, cheap)

GET /API/V3/PM/Projects/"…A","…B"
    -> [ {…full project A…}, {…full project B…} ]                      (page 1, max 100 IDs)

If all you need is a label, an icon or a code — a picker, a breadcrumb, a tooltip — do not read the full object. Use the identification endpoints of section 5.5 instead; they are a fraction of the payload.

5.3 Bulk endpoints and partial success

Every get<Objects>/:ids endpoint, and every other endpoint taking an array of IDs, is a bulk endpoint. Two rules matter:

  • Permission is evaluated per element. Elements the user may not touch are dropped, and the call still succeeds for the rest. A bulk read with one forbidden ID returns 200 and a shorter array — not 403.
  • Per-element outcomes are reported in the X-Detail-Codes response header where the endpoint provides them, as comma-separated integers using the same values as HTTP status codes. Read it when you need to know which element failed and why. The overall status code follows the "partial success is success" rule: if any element succeeded, the call succeeded.

Because elements can be dropped, never assume the result array is the same length as the ID array, or in the same order. Match results back to your input by the id in each element, not by position.

X-Detail-Codes is not restricted to reads or to ID arrays, either: a write whose request body has parts that are written independently reports one status per part, in the order the body lists them. alc.updateResourceShiftPlan is the case to know — it answers one code per slot of the planning matrix, 400 for every slot its update modes refused to write (section 10, ALC).

The bulk maximum (pqforce.performance.bulk.max, default 100) is enforced as an assertion, so exceeding it is rejected with 422 rather than truncated. It is configured per instance — read it at runtime (section 5.13).

5.4 Sorting — new in V3

V2 had no sort parameter anywhere. In V3, 128 endpoints — the getAll…, get…Of… and get…By… lists and most other lists of references — take an optional sort array.

  • One element per criterion, applied in order: name, name:asc or name:desc (ascending is the default).
  • Up to pqforce.performance.sort.max criteria per request — a configured limit whose default is 8, readable at runtime (section 5.13).
  • The properties allowed on a given endpoint are enumerated in that endpoint's sort description, as “Possible properties: …”. The common set is type, id, name, code, description, color, createdAt, modifiedAt, and individual endpoints add their own (sortIndex, validityStart, …).
  • Dynamic (tenant-configured) properties can be sorted on too, by their key, when their semantics are sortable: string, UUID, integer, long, double, boolean, duration, point in time, day, currency amount, URI and colour.
  • An unknown or malformed criterion is rejected with 422.
GET /API/V3/PM/Projects?sort="code","name:desc"

If you sorted lists client-side in V2 because there was no alternative, this is the first thing to delete.

5.5 Identifications — display data without the object

Object families that V3 models as a full CRUD family expose …/:id/Identification (one object) and …/:ids/Identification on the plural route (several) — 40 such endpoints today. The payload is pf.Identification: type, id, name, code, description, color, iconRef. Nothing else — no dynamic properties, no timing information, no relations.

The bulk variant takes an optional deleted flag and answers with pf.IdentificationExtended, which adds a deleted member — so a stale reference in your own data can be labelled rather than silently dropped.

Use it for anything that renders a reference: pickers, link labels, breadcrumbs, and — importantly — resolving the createdBy / modifiedBy references described in section 5.10.

5.6 Version history — Timeline and Version

For versioned object types, V3 exposes history uniformly:

  • GET …/:id/Timeline?limit=&from=&until=&granularity= — the list of versions in a period, at a chosen calendar granularity.
  • GET …/:id/Version/:versionId — one historic version, in the same contract as the current one.

V2 exposed history through per-module get…History endpoints with per-module response shapes. Those either redirect or are reimplemented as the two endpoints above; section 10 says which.

5.7 Waste bin — Deleted/Recently and Undelete

Where an object type supports it, V3 offers GET …/<Objects>/Deleted/Recently and POST …/<Object>/:id/Undelete. Both require the undelete licence feature and the corresponding permission, and both are limited to the tenant's undelete window — an object deleted longer ago than the window is neither listed nor restorable (404).

The window is part of the tenant's licence, not a fixed number of days. Read it with

GET /API/V3/PF/UndeleteLimit   -> PF.UndeleteLimit { days }

which needs the same undelete licence feature and answers 402 without it. Do not assume a default: use the value to decide whether offering "restore" to your user makes sense at all.

5.8 Creating objects — Add with basic field initializers

In V2, creating an object was usually two calls: POST …/Add returned a reference to a default-named object, and you then issued a PUT to give it a name, a code, a description, a colour and an icon.

In V3, 31 of the 41 add… endpoints accept those basics as optional query parameters, in a consistent order:

POST /API/V3/PM/Project/:projectId/Scenarios/Add?name=Baseline&code=BL&color=%23336699
POST /API/V3/MTG/MeetingGroups/Add?name=Steering%20board&iconRef=mdi:account-group
POST /API/V3/RES/Resource/:parentResourceId/AddChild?linkTypeId=…&name=New%20team
Parameter Meaning
name The name of the new object. Omit it and the object gets a localized default name.
code Short code, at most 32 characters.
description Descriptive text, may contain HTML.
color A colour value.
iconRef The icon: /images/…png on the server, /API/V3/PF/Icon/<id> for an uploaded icon, a data:image/…;base64,… inline image, or mdi:<name> for a Material Design icon.

Individual endpoints add their own initializers next to these — start / end, sortIndex, periodStart / periodEnd, currency, and the copy… flags that control what is carried over when creating from a template.

The return value is unchanged from V2: a pf.Reference to the new object, with 201 Created. So the migration is a simplification — fold your follow-up PUT into the POST, and keep the PUT only for the fields the Add endpoint does not offer.

One naming note: the icon member is iconRef throughout V3. If your V2 code still writes an imageUrl member on one of the few contracts that had one, rename it.

5.9 PUT does not always create

In V3, PUT /…/<Object>/:id updates an existing object, and only creates one where the endpoint documents that it may. Where it may not, a PUT to an unknown ID answers 404, and the object has to be created through the module's POST …/Add endpoint. The permission checked is UPDATE when the object exists and CREATE when it does not.

If your V2 integration invents its own IDs and creates objects by PUT, check each endpoint before relying on it.

5.10 Data contracts — a cleaner hierarchy

The generic base contracts were rebuilt. The change is small in size and easy to miss in effect.

V2 V3 Members
pf.Reference pf.Reference type, id — unchanged
pf.Item pf.Identification name, code, description, color, iconRef
pf.VersionedReference pf.VersionReference / pf.VersionedReference version; plus historic, createdAt, createdBy, modifiedAt, modifiedBy
pf.VersionedItem pf.BasicObject versioned reference + the five identification members
pf.VersionedItemWithProperties pf.CustomizedObject pf.BasicObject + properties

Three differences bite in practice:

  1. createdBy and modifiedBy are now bare references. In V2 they were full items, so modifiedBy.name was there for the taking. In V3 they are pf.Reference — type and id only. Resolve the name through the user's identification endpoint, and cache it: in a list of 100 rows, one bulk identification call replaces 100 lookups.
  2. Validity left the generic contract. V2's pf.Item carried validityStart and validityUntil for every object, whether or not it had a validity period. In V3 validity is declared only on the objects that actually have one, under the same names validityStart and validityUntil.
  3. Translated variants are explicit. Where a contract exists in a translated form, its name, description and iconRef are arrays of per-language values rather than a single resolved string. The untranslated contract still resolves to the request's Accept-Language.

5.11 Assertions and 422

Assertions (section 3.3) are pervasive in V3 — roughly five times as many declared constraints as in V2. The practical effect is that malformed input is rejected earlier and more consistently:

  • A value that violates its assertion — a code longer than 32 characters, a sort criterion that is not a known property, a bulk array over the maximum, a malformed icon reference — is answered with 422, before any business logic runs.
  • A missing mandatory parameter, or one that cannot be parsed into its declared type at all, is answered with 400.
  • A body that is not valid JSON, or does not match the declared structure, is answered with 422.

Read the Assertion column in the reference documentation for each parameter you send, and validate on your side. It is cheaper than a round trip, and the server deliberately does not tell you which rule you broke.

5.12 Return codes and response headers

The codes V3 uses:

Code When
200 Request succeeded
201 A new object was created (all add… endpoints, and a creating PUT)
202 Accepted, processing not yet complete
304 Cached copy still valid (static requests with If-None-Match)
307 A deprecated or alias URL; the effective URL is in Location. This is what a redirected V2 endpoint answers.
308 As 307, permanently
400 Missing or unparsable parameter or header
401 Authentication failed
402 A licensing restriction: the endpoint, an optional parameter, or an expired licence
403 No permission for the endpoint or the object (also returned when the object does not exist)
404 Unknown endpoint, or an object that does not exist or was deleted
415 Missing or wrong request body type
422 Assertion violation, malformed body, or a sanitizer violation
429 Too many requests in parallel
500 Server-side failure
503 Server is in maintenance mode

Three headers to handle that you may have ignored in V2:

  • Location — on 307/308. Following it is mandatory for redirected V2 endpoints (section 2.1).
  • X-Detail-Codes — per-element results of a bulk call (section 5.3).
  • Deprecation — present on every response from a deprecated endpoint, or from a call that used a deprecated parameter. Log it. It is the cheapest early warning you will get, and it is how you find the V2 calls still hiding in your code. In the JavaScript API the equivalent is a warning in the JavaScript console.

Note 429: parallel requests are limited per token (and per user, per session, per address), and the limits are configured per instance. An integration that fans out bulk reads should read its own ceiling and respect it rather than rely on retries:

GET /API/V3/PF/Limits/MaxParallelRequests   -> PF.ParallelRequestsLimit { maxParallel }

The endpoint reports the limit that applies to the caller as it is calling — the per-token limit when you authenticate with a bearer token, the per-user limit otherwise — so a public-API integration and a browser session get different answers from the same URL. Two things changed here from V2, and there is no redirect: the URL segment is Limits, not Limitations, and the response member is maxParallel, not limit.

5.13 Read the limits, do not hard-code them

Every numeric limit quoted in this guide is a configured default. It can differ on a dedicated or on-premises instance, and it can change with a release — the defaults below are the base configuration layer, which every instance may override. Read the values at start-up and cache them for the lifetime of your process; do not compile them in.

There are three endpoints, because the limits come from three different places.

What you need Endpoint Answers
A shared server setting GET /API/V3/PF/ServerConfig/:configKey text/plain, the raw value; 404 for a key that is not published
Your own parallel-request ceiling GET /API/V3/PF/Limits/MaxParallelRequests PF.ParallelRequestsLimit { maxParallel }
The tenant's undelete window GET /API/V3/PF/UndeleteLimit PF.UndeleteLimit { days }; needs the undelete licence

ServerConfig publishes a fixed set of keys — anything else answers 404, so it is not a way to read arbitrary server configuration. The defaults below come from the base configuration layer; an instance may override any of them, and a key it does not configure at all is not published — so a 404 on a key from this list means "not configured on this instance", not "wrong key". The keys:

Key Default What it limits
pqforce.performance.bulk.max 100 Elements in one bulk parameter array (sections 5.2, 5.3)
pqforce.performance.sort.max 8 Sort criteria per request (section 5.4)
pqforce.performance.attachment.max 20971520 Attachment upload size, in bytes
pqforce.performance.ganttprojectlimit 500 Projects in one Gantt chart
pqforce.planningrange.from / .to -2 / 2 Years, relative to the current one, in which allocations may be planned
pqforce.dayrange.from / .to -10 / 10 Years, relative to the current one, in which day-based planning is available
pqforce.dayrange.max 731 Days one request may be answered a slot per day for; a wider range is refused with 400
pqforce.security.passwordrule.regex — The password rule, as a regular expression
pqforce.security.passwordrule.desc.<language> — The password rule in words, for display; one key per available language
pqforce.security.contentSecurity.binaries.fileExtensionWhitelist / …Blacklist — Accepted / rejected upload file extensions
pqforce.security.contentSecurity.binaries.mimeWhitelist / …Blacklist — Accepted / rejected upload MIME types
pqforce.security.contentSecurity.text.enabled — Whether text bodies are content-checked
pqforce.security.contentSecurity.json.enabled — Whether JSON bodies are content-checked
pqforce.website, pqforce.login, pqforce.support — The instance's public web address, login service URL and support address

Validate an upload against the extension, MIME and size keys before you send it, and the password rule before you submit a password — all three are rejections you can avoid entirely.

The two other limits are not ServerConfig keys, and that is deliberate: the parallel-request ceiling depends on how you authenticate and the undelete window comes from the licence, so neither has one shared value to publish. Use their own endpoints, above.

Do not confuse ServerConfig with Config. GET /API/V3/PF/ServerConfig/:configKey reads a setting the whole instance shares and is read-only. GET /API/V3/PF/Config/:configId reads a tenant setting, addressed by a known configuration ID rather than a dotted key, and has PUT and DELETE counterparts. Neither accepts an arbitrary key: both answer 404 for an identifier they do not publish.

Inside a JavaScript automation the runtime limits are reachable without a request at all: sandbox.remainingRuntimeMs() reports the time left in the current execution, against the configured per-execution and hourly quotas.

Finally, check compatibility once at start-up with GET /API/V3/MAIN/Versions, which reports the server release and the API version. All four of these calls belong in the same start-up step.

5.14 Change notifications over WebSocket

V2 had a single WebSocket, handed out by getNewWebSocketConnection (POST /API/V2/PF/WebSocket/Create) and shared by the PQFORCE browser client and the public API. V3 splits it in two. The browser client gets a WebSocket of its own, which is out of scope here. Integrations get a change-notification WebSocket, which is reserved for public-API access, that is, for agents and interface components.

POST /API/V3/PF/ChangeNotification/CreateNew   -> PF.WebSocketInfo { url, validUntil }

Call it, then open the returned URL — note the limited validity, so open it promptly and re-request when it expires. The socket is effectively one-directional: the server does not accept messages (an empty {} is tolerated), and sends JSON messages of two kinds:

Message Meaning
{"sessionInfo": … } Sent once, describing the session
{"dataChange": … } An object was created or changed: action, the object's own reference in data, and its parents in parent

The V2 call redirects here. getNewWebSocketConnection answers 307 with ChangeNotification/CreateNew, so the URL it returns now opens the change-notification WebSocket. This has two consequences for code that stays on the V2 call:

  • Bearer token only. With a session cookie, the redirect target answers 404.
  • Change notifications only. The socket sends sessionInfo and dataChange and nothing else. The concurrency messages of the V2 socket (featureConcurrency, accessConcurrency) are no longer sent, and the feature and access messages a client used to send are no longer accepted.

If your integration currently polls a list endpoint on a timer, this is the change that removes the timer. Note that a notification tells you that an object changed; you still read the new state through the ordinary endpoints.

5.15 Dynamic properties — definition, presentation, and the order you read them in

Dynamic (tenant-configured) properties are the one area where V3 did not rename an endpoint but split a concept. Every V2 property call maps onto the new shape, so read this before you rewrite them; the endpoint-by-endpoint mapping is in the PF part of section 10.

One contract became two. V2's PropertyDefinition described in a single object both what a property is and how a client shows it. V3 separates the two, because the first is a fact about the tenant's data model and the second is one client's opinion about it:

V2 PropertyDefinition member V3 home Why
id, objectType, semantics PF.PropertyDefinition The property itself — shared by every client
sortIndex, label, identifier, hint, constraint PF.PropertyPresentation How one client presents it

PropertyPresentation extends PropertyDefinition and adds clientId, so a presentation still carries the definition's members — you rarely need both calls to display something.

The client is part of the URL. Every V2 property endpoint has a presentation endpoint as its successor, and the client is a path segment. Pass Client4, the PQFORCE browser client, to get what V2 returned: V2 read the Client4 presentation internally and never told you.

V2   GET /API/V2/PF/ObjectType/Project/PropertyDefinitions
V3   GET /API/V3/PF/PropertyPresentations/OfClient/Client4/OfObjectType/Project

Writing is two calls, in order. putAllPropertyDefinitions replaces the definitions of an object type — a definition missing from the list is deleted, and that fails as long as any client still presents it. putAllPropertyPresentationsTranslated replaces what one client presents — every definition must already exist, nothing is ever deleted, and a property missing from the list only loses this client's presentation. So create definition first, presentation second; when removing, drop the presentation first and the definition after. Both need the config-properties licence feature and the configuration permission, where V2 had one call.

Values became typed. V2's Property was two strings: a key and a value that you parsed yourself. In V3 value is PF.PropertyValue, a choice contract with one member per semantics — string, uuid, int, long, double, bool, uuidArray, duration, datetime, date, currencyAmount, uri, color, json — and the definition's semantics tells you which member is filled. key is the property definition's ID, now typed uuid; it still carries the readable keys you know from V2. A property the object has no value for is present with no value.

The default sorting of a live object's properties is by definition ID — not by sortIndex. This is the part that silently breaks a port. On a live object — a project, a resource, a work item — the properties array is ordered by property definition ID, ascending and stable. It cannot be ordered by sortIndex, because sortIndex belongs to a client and an object body is the same for every client.

  • This is a change, and it lands on V2 as well. Until the split, sortIndex was a member of the definition itself, and the shared definition list — the template from which every object's properties array is built — was ordered by it. Since the split that list is ordered by definition ID, and so is the properties array of every object, in V2 responses just as much as in V3: both read the same list. A V2 client that walks the array positionally against getPropertyDefinitions is already reading the wrong property. Match by key.
  • getPropertyDefinitions itself still answers in sortIndex order — V2 serves it from the Client4 presentation. What changed is the relationship between the two lists, not that endpoint.
  • In V3 the definition list and the object agree again. getAllPropertyDefinitions(objectType) answers in definition-ID order — the same order as every object of that type, one entry per definition, element for element.
  • The display order is a separate call. getAllPropertyPresentations(clientId, objectType) answers in that client's sortIndex order (ties broken by ID). Use it to lay out a form or a column set, and join it to the object's properties by key — do not re-order the object's array to match, and do not assume the n-th property of an object is the n-th presentation.
  • Reading a single property never depends on either order: getPropertyDefinition(definitionId) and getPropertyPresentation(definitionId, clientId) address it directly.

Sorting objects by a property works on the same key (section 5.4). Pass a property definition ID in sort and the comparison follows that property's declared semantics. Two rules are easy to miss: a dynamic property is only sortable when the request concerns a single object type — with several types, only the fixed properties are — and a value the caller may not READ is treated as empty, so those objects group at the start of an ascending sort and at the end of a :desc one.


6. The work-item type: Phase becomes ProjectWorkItem

A node of the project work breakdown structure — what users call a task — is identified by an object type ID. That ID used to be Phase, which collided with project-management methodology phases and matched nothing users say. As of 4.26.6 the type is called ProjectWorkItem.

Which spelling you get depends on the endpoint's generation, not on your code:

You call You send and receive
An API V2 endpoint Phase — unchanged, indefinitely, until V2 is switched off
An API V3 endpoint ProjectWorkItem
A redirected V2 endpoint (section 2.1) you may send Phase; the response is a V3 response and says ProjectWorkItem

The server translates the old spelling inbound at the V2 boundary, which is what makes the change a low-impact upgrade. It is also why the change surfaces at the end of 2028: everything still relying on the translation breaks when V2 goes.

What to check in your own code:

  • Type comparisons. Anywhere you compare an object type against the literal "Phase", decide which generation the value came from. A V2 response legitimately says Phase; a V3 response says ProjectWorkItem. Code that handles both should accept either.
  • Mixed-generation code paths. If one part of your integration reads through V2 and another writes through V3, do not pass a type value from one to the other unchanged.
  • Stored type values. Configuration, filters, mappings or downstream tables that recorded Phase as text still say Phase. They keep working while V2 lives. Plan to update them.
  • Deep links. Links that embed the object type and were created before 4.26.6 still carry Phase and still work, because the server accepts the old spelling. They stop working when V2 is retired — worth regenerating if you store PQFORCE deep links in another system.
  • Endpoint names. In the PM module several V3 endpoints were renamed to spell the object out in full: moveWorkItem → moveProjectWorkItem, splitWorkItem → splitProjectWorkItem, copyWorkItemTree → copyProjectWorkItemTree, addWorkItemSubWorkItem → addProjectWorkItemSubWorkItem, getWorkItemSubWorkItems → getSubWorkItemsOfProjectWorkItem. The URLs are unchanged except for the sub-work-item pair, which is now …/SubWorkItems and …/SubWorkItems/Add. The V2 names are untouched.

Note that this affects the type ID, not your data: permissions, lifecycle states, relations, documents, dynamic properties and backups are migrated by the upgrade itself.


7. The JavaScript API

7.1 Choosing a generation

Every JavaScript source has an API generation attribute. It decides what the root object Pqf points at:

  • In a V2 source, Pqf.* is the V2 API.
  • In a V3 source, Pqf.* is the V3 API.
  • PqfV2 and PqfV3 are always both available, in either kind of source.

That gives you two migration strategies:

  • Gradual, inside a V2 source: leave the source on V2 and rewrite call sites one at a time to PqfV3.…. The rest of the script keeps working. This is the low-risk route for a long script.
  • All at once: switch the source to V3 and change every Pqf.… call that has moved. Use PqfV2.… explicitly for the few endpoints that are not migrated yet.

Either way, be explicit about the generation of any type value you move between the two — see section 6.

7.2 What changes in a call

  • Renames are hard breaks. There is no redirect for a function name. Calling a V3 function by its old name fails with “not a function”. Section 10's tables are the rename list.
  • Optional parameters are overloads. A V3 function is declared once per omitted trailing optional parameter. You can omit trailing arguments, but to skip a middle one you pass null.
  • Deprecated calls warn. Calling a deprecated endpoint writes a warning into the JavaScript console. Run your automations once and read the console — it is a free inventory of what still needs migrating.
  • Errors are unchanged in shape. A failed request throws; the message starts with # and a three-digit code matching the HTTP status codes of section 5.12. try … catch as before, and switch on the code.
  • Bulk calls behave as over HTTP. Elements you may not read are dropped from the result array. Match results by id, not by index.

7.3 Small but easy to miss

  • UUID generation. V2's clf.newUuids endpoint is not available to public-API or JavaScript callers in V3. In a script use Uuid.generate(), which needs no request at all.
  • HTML sanitizing. V2's clf.sanitizeHtml endpoint is retired; use Html.sanitize(…), alongside Html.encode(…) / Html.decode(…).
  • Everything else in the sandbox is unchanged — console, store, sandbox, Uuid, Html, JSON, Date, Math, and the optional moment library. Storage keys stay per script.
  • Typed authoring. Pull the TypeScript definition of section 3.2 into your editor before you start rewriting; it catches renamed functions and changed parameter lists as you type.

8. End-of-life modules and functionality

Three whole modules and three groups of endpoints are retired rather than migrated. All of them keep working until the end of 2028.

8.1 DOC — legacy document exports (whole module retired)

All eight DOC endpoints — export configurations, export templates, available exports and the export request handler — are discontinued. Use the EXP module with JavaScript exports instead:

PUT  /API/V3/EXP/JavaScriptExportDefinition/:id/WithTranslations   putExportDefinitionTranslated
GET  /API/V3/EXP/JavaScriptExportDefinitions                      getAllExportDefinitions
GET  /API/V3/EXP/ObjectType/:objectType/JavaScriptExportDefinitions
                                                                  getExportDefinitionsForObjectType
POST /API/V3/EXP/JavaScriptExport/:id/:objectType/:objectId       handleJavaScriptExportRequest
POST /API/V3/EXP/JavaScriptExport/:id/:objectType/:objectId/WithSettings
                                                                  handleJavaScriptExportRequestWithSettings

The EXP model is more capable: the export logic is a JavaScript automation you control, rather than a template the server interprets. Note that the V3 export definitions are named JavaScriptExportDefinition in the URL and are written through the translated variant, so a definition always carries its language versions.

8.2 DPO — disposition views (whole module retired)

The eight disposition-view endpoints — reading, creating, moving, deleting and reassigning dispatch items by resource, and the absence-block view — have no V3 successor. The one exception is delWorkItem, which is redirected to pm.delProjectWorkItem. Build the equivalent view from the PM work-item endpoints and the allocation endpoints once ALC is migrated.

Eight of the ten NAV endpoints are discontinued. getRootContext, getContext and getObject modelled the tree navigation of the old browser client and go with it; getTypeInfos described the types behind that navigation. The structured and member searches (findObjects, getMemberTextSearch, getMemberRangeSearch, getMemberRegexSearch) are retired with them.

The exception is the full text search: getContextSearch and getSearch are replaced by nav.search (POST /API/V3/NAV/Search?query=...). It searches all objects of all types known to the navigation, takes the plain text query only (HTML in it is decoded), and returns PF.ReferenceWithParent — the object found and its parent (none where that is a navigation-only entry), one entry per object — sorted with the usual sort parameter. There is no start object, no type restriction and no regular expression; filter the result by type where you need one type only. Only objects the caller may identify are returned, and navigation-only entries of the old client (modules, lists) are not, since they are no objects of V3.

8.4 ACT — single-time-per-day endpoints (retired)

Seven endpoints that read, wrote or deleted the project time or the project-and-task time of a resource for a day are discontinued, because a resource may book more than one time entry per project, task and day — so these endpoints could never represent the data faithfully.

Move to the time-actual family, which models the real cardinality. It is in V3 now, as a full object family under the resource that booked the time: getResourceProjectTimeActual, putResourceProjectTimeActual, delResourceProjectTimeActual and addResourceProjectTimeActual for the single record; getResourceProjectTimeActuals (bulk by ID), getAllResourceProjectTimeActuals (a day range) and getAllResourceProjectTimeActualsByPeriod (one actuals period) for the lists; getProjectResourcesProjectTimeActuals for the view from the project; and getResourceProjectTimeActualTimeline / getResourceProjectTimeActualVersion for the history. Section 10 names the V2 predecessor of each.

8.5 Endpoint-level retirements

The rest are individual endpoints whose functionality is covered by a general mechanism — object relations instead of a bespoke link endpoint, the sanitizer helper instead of an endpoint call, a translated variant instead of an untranslated one, the object endpoints instead of a "details" projection. Section 10 lists each of them with its own advice, per module.


9. A worked example

Reading the projects of a tenant and showing them in a sortable, paged table.

In V2 — one call, everything, unordered:

GET /API/V2/PM/Projects
    -> PM.Project[]   (every project the user may read, in full)

Sorting and paging happen in your code, and the payload grows with the tenant.

In V3 — references, then pages, ordered by the server:

GET /API/V3/PM/Projects?sort="name"
    -> PF.ReferenceWithParents[]   (every project the user may identify, ordered)

GET /API/V3/PM/Projects/"<id1>","<id2>",…"<id100>"
    -> PM.Project[]                (the first page, at most 100)

In V3, if the table only shows name, code and icon — skip the full read entirely:

GET /API/V3/PM/Projects/"<id1>",…,"<id100>"/Identification
    -> PF.Identification[]         (name, code, description, color, iconRef)

Creating a project inside a portfolio — V2 needed two calls, V3 needs one:

V2:  POST /API/V2/PM/ProjectPortfolio/:portfolioId/Projects/Add
     PUT  /API/V2/PM/Project/:newProjectId          (to set name, code, …)

V3:  POST /API/V3/PM/ProjectPortfolio/:projectPortfolioId/Projects/Add?name=Apollo&code=APO
     -> 201, PF.Reference

The same reads in a JavaScript automation:

var refs = PqfV3.pm.getAllProjects(['name']);                                   // references, ordered
var ids  = refs.slice(0, 100).map(function (r) { return r.id; });
var page = PqfV3.pm.getProjects(ids);                                           // one page of details
var idns = PqfV3.pm.getProjectIdentifications(ids);                             // or just the labels

Optional parameters become trailing overloads, so a null is only needed to skip a parameter that is followed by one you do want to pass:

var group = PqfV3.mtg.addMeetingGroup(null, null, 'Steering board', 'SB');       // copy, copyRels skipped

10. Per-module endpoint guide

How to read the tables:

  • Paths omit the common prefix. A V2 path is relative to /API/V2/<MODULE>/, a V3 path to /API/V3/<MODULE>/. When the V3 endpoint lives in a different module (written mod.name), its path starts with that module instead (ALC/Resource/…).
  • JS only means the endpoint has no HTTP route and exists only in the JavaScript API.
  • A V3 endpoint written as mod.name lives in a different module than the V2 endpoint.
  • Redirected tables: the V2 URL answers 307; nothing else changes. Move at your convenience.
  • Reimplemented tables: there is no redirect. What differs is the shortest accurate summary — check the reference documentation for the endpoint before rewriting.
  • Retired tables carry the endpoint's own deprecation note.
  • New in V3: capability that had no V2 equivalent, listed so you can find it.

ACM — Access control, users, tokens, single sign-on

Almost every ACM response contract was rebuilt around pf.Reference and pf.Identification, so expect to touch your response parsing even where the URL is unchanged.

  • The SAML 2 URLs are spelled Saml2/IdP (capital P) and the path parameter is :authorityId. Both are covered by the redirect, so nothing breaks until you move.
  • The e-mail notification flag no longer has its own endpoint: getCurrentUserReceiveNotifications and putCurrentUserReceiveNotifications fold into get/putCurrentUserPreferences, where it is one member of the preferences object.
  • getUsers and getSaml2IdentityProviders no longer return everything. Use getAllUsers / getAllSaml2IdentityProviders for the reference list, then the plural getter with the IDs you want.
  • getPermissionCubeForType and getPermissionCubeForId both fold into getPermissionCubeForObject.
  • User relations move to the general relation mechanism in PF — see pf.getAllRelations.
  • getUserCertificate is now getCurrentUserCertificate, matching the CurrentUser URL it always had.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
addSaml2IdentityProvider POST Saml2/Idps/Add addSaml2IdentityProvider POST Saml2/IdPs/Add
apiTokenUnauthorize POST ApiToken/:tokenid/Unauthorize apiTokenUnauthorize POST ApiToken/:tokenId/Unauthorize
createNewApiToken POST ApiToken/Create createNewApiToken POST ApiToken/Create
delSaml2IdentityProvider DELETE Saml2/Idp/:authid delSaml2IdentityProvider DELETE Saml2/IdP/:authorityId
delUser DELETE User/:userId delUser DELETE User/:userId
delUserRoleAuthorizations DELETE UserRole/:userRoleId/Authorizations delUserRoleAuthorizations DELETE UserRole/:userRoleId/Authorizations
delUserRoleTranslated DELETE UserRole/:userRoleId/WithTranslations delUserRoleTranslated DELETE UserRole/:userRoleId/WithTranslations
getApiTokenInfo GET ApiToken/Info getApiTokenInfo GET ApiToken/Info
getAuthorizedApiToken GET AuthorizedApiToken/:tokenId getAuthorizedApiToken GET AuthorizedApiToken/:tokenId
getAuthorizedApiTokens GET AuthorizedApiTokens getAuthorizedApiTokens GET AuthorizedApiTokens
getPermission GET Permission/:permissionId getPermission GET Permission/:permissionId
getPermissions GET Permissions getPermissions GET Permissions
getSaml2IdentityProviderRequestSignCertificate GET Saml2/Idp/:authid/RequestSignCertificate getSaml2IdentityProviderRequestSignCertificate GET Saml2/IdP/:authorityId/RequestSignCertificate
getUserCertificateBinary GET CurrentUser/Certificate/Binary getCurrentUserCertificateBinary GET CurrentUser/Certificate/Binary
getWelcomeMessage GET Welcome getWelcomeMessage GET Welcome
putApiTokenInfo PUT ApiToken/Info putApiTokenInfo PUT ApiToken/Info
renewSaml2IdentityProviderRequestSignCertificate POST Saml2/Idp/:authid/RequestSignCertificate/Renew renewSaml2IdentityProviderRequestSignCertificate POST Saml2/IdP/:authorityId/RequestSignCertificate/Renew

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
addUser POST Users/Add/:firstAuthority addUser POST Users/Add/:firstAuthorityId Returns a reference to the new user instead of the full user
getAuthorizationByObj GET CurrentUser/Authorizations/:objectType/:objectId getAuthorizationByObj GET CurrentUser/Authorization/:objectType/:objectId Names the transition transitionId
getAuthorizationByPerm GET CurrentUser/Authorizations/:permissionId getAuthorizationByPerm GET CurrentUser/Authorization/:permissionId The permission description is not included, read it with getPermission
getAuthorizationsByObj GET CurrentUser/Authorizations/:objectType/:objectId/All getAuthorizationsByObj GET CurrentUser/Authorizations/:objectType/:objectId Names the transition transitionId
getConfidentiality GET Confidentiality/:objectType/:objectId getConfidentiality GET Confidentiality/:objectType/:objectId Expects the V3 object type (ProjectWorkItem instead of Phase)
getCreateAuthorizationByObj GET CurrentUser/Authorizations/:objectType/:objectId/Create/:createObjectType getCreateAuthorizationByObj GET CurrentUser/Authorization/:objectType/:objectId/Create/:createObjectType Names the transition transitionId
getCurrentUser GET CurrentUser getCurrentUser GET CurrentUser Reports the user name as login
getCurrentUserAuthorizations GET CurrentUser/Authorizations getCurrentUserAuthorizations GET CurrentUser/Authorizations The permission descriptions are not included, read them with getPermissions
getCurrentUserPreferences GET CurrentUser/Preferences getCurrentUserPreferences GET CurrentUser/Preferences Response body changed
getCurrentUserReceiveNotifications GET CurrentUser/ReceiveEMailNotifications getCurrentUserPreferences GET CurrentUser/Preferences Parameter list changed
getFirstFactorAuthorities GET FirstFactorAuthorities getFirstFactorAuthorities GET FirstFactorAuthorities Response body changed
getFirstFactorAuthority GET FirstFactorAuthority/:authorityId getFirstFactorAuthority GET FirstFactorAuthority/:authorityId Response body changed
getPermissionCubeForId JS only getPermissionCubeForObject GET PermissionCube/:objectType/:objectId Response body changed
getPermissionCubeForObject GET PermissionCube/:objectType/:objectId getPermissionCubeForObject GET PermissionCube/:objectType/:objectId Expects the V3 object type (ProjectWorkItem instead of Phase) and returns user references and lifecycle state IDs instead of full objects
getPermissionCubeForType GET PermissionCube/:objectType getPermissionCubeForObject GET PermissionCube/:objectType/:objectId Parameter list changed
getPermissionCubeMappingsForType GET PermissionCubeMappings/:objectType getPermissionCubeMappingsForType GET PermissionCubeMappings/:objectType Expects the V3 object type (ProjectWorkItem instead of Phase) and returns relation type, state and transition IDs instead of full objects
getPermissionsGraph GET PermissionGraph getPermissionGraph GET PermissionGraph Names the parent permissions parentIds
getSaml2IdentityProvider GET Saml2/Idp/:authid getSaml2IdentityProvider GET Saml2/IdP/:authorityId Names the e-mail field emailField
getSaml2IdentityProviders GET Saml2/Idps getAllSaml2IdentityProviders GET Saml2/IdPs Returns references; read them with getSaml2IdentityProviders
getSecondFactorAuthorities GET SecondFactorAuthorities getSecondFactorAuthorities GET SecondFactorAuthorities Response body changed
getSecondFactorAuthority GET SecondFactorAuthority/:authorityId getSecondFactorAuthority GET SecondFactorAuthority/:authorityId Response body changed
getUser GET User/:userId getUser GET User/:userId Reports the user name as login
getUserAuthorizations GET User/:userId/Authorizations getUserAuthorizations GET User/:userId/Authorizations The permission descriptions are not included, read them with getPermissions
getUserCertificate GET CurrentUser/Certificate getCurrentUserCertificate GET CurrentUser/Certificate Response body changed
getUserIdentification GET User/:userId/Identification getUserIdentification GET User/:userId/Identification Parameter list changed
getUserRole GET UserRole/:userRoleId getUserRole GET UserRole/:userRoleId Response body changed
getUserRoleAssignments GET User/:userId/Roles getUserRoleAssignments GET User/:userId/Roles Returns references to the assigned user roles
getUserRoleAuthorizations GET UserRole/:userRoleId/Authorizations getUserRoleAuthorizations GET UserRole/:userRoleId/Authorizations Names the permission permissionId
getUserRoles GET UserRoles getUserRoles GET UserRoles Response body changed
getUserRolesTranslated GET UserRoles/WithTranslations getUserRolesTranslated GET UserRoles/WithTranslations Response body changed
getUserRoleTranslated GET UserRole/:userRoleId/WithTranslations getUserRoleTranslated GET UserRole/:userRoleId/WithTranslations Response body changed
getUsers GET Users getAllUsers GET Users Returns references; read them with getUsers; the name, resourceId, firstAuthorityId, email and active filters are gone
getUserSecondFactorStatus GET User/:userId/SecondFactor/Status getUserSecondFactorStatus GET User/:userId/SecondFactor/Status Response body changed
putConfidentiality PUT Confidentiality/:objectType/:objectId putConfidentiality PUT Confidentiality/:objectType/:objectId Expects the V3 object type (ProjectWorkItem instead of Phase)
putCurrentUserPreferences PUT CurrentUser/Preferences putCurrentUserPreferences PUT CurrentUser/Preferences Request body changed
putCurrentUserReceiveNotifications PUT CurrentUser/ReceiveEMailNotifications putCurrentUserPreferences PUT CurrentUser/Preferences Request body changed
putSaml2IdentityProvider PUT Saml2/Idp/:authid putSaml2IdentityProvider PUT Saml2/IdP/:authorityId Names the e-mail field emailField
putUser PUT User/:userId putUser PUT User/:userId Takes the user name as login
putUserRoleAssignments PUT User/:userId/Roles putUserRoleAssignments PUT User/:userId/Roles Takes references to the user roles and requires the role management license (API V2 accepted an assignment of all roles without it)
putUserRoleAuthorizations PUT UserRole/:userRoleId/Authorizations putUserRoleAuthorizations PUT UserRole/:userRoleId/Authorizations Body names the permission permissionId
putUserRoleTranslated PUT UserRole/:userRoleId/WithTranslations putUserRoleTranslated PUT UserRole/:userRoleId/WithTranslations Request body changed

Retired — no V3 successor; the endpoint disappears with API V2.

V2 endpoint V2 path Note
getCurrentUserRelations GET CurrentUser/Relations Use endpoints in PF module, will be discontinued at end of 2028
getUserRelations GET User/:userId/Relations Use PF endpoints, will be discontinued at end of 2028

New in V3 — no V2 equivalent.

getFirstFactorAuthorityUsers, getUserIdentifications, getUserPreferences, putUserPreferences

ACT — Actual times and presences

  • Version history follows the general pattern: the …History endpoints become …Timeline plus …Version/:versionId.
  • getActualsPeriods (all periods) becomes getAllActualsPeriods; the plural-with-IDs routes are new.
  • The seven single-time-per-day endpoints are retired — see section 8.4 for the replacement family.
  • The efforts endpoints are in V3, reshaped: ProjectEfforts.id becomes projectId and the contract gained a resourceId, so one ResourceProjectEfforts serves both directions. getProjectResourcesEfforts is the new mirror image — the efforts of several resources in one project. All three take an optional beg/end day range (end exclusive); omit both and the answer spans the whole allocation, as V2 always did. Giving only one of the two is refused, and both days must lie within the configured planning range.
  • The phase-detailed efforts are the same answer plus a breakdown. getResourceProjectWithWorkItemEfforts and getResourceProjectsWithWorkItemEfforts hang off the plain efforts URL as Efforts/WithWorkItems and answer a ResourceProjectWithWorkItemEfforts, which extends ResourceProjectEfforts. Its planned/actual/remaining are therefore the same totals the plain endpoints answer, and its workItems array adds one entry per work item — including the entry naming no workItemId, which carries what was allocated on the project itself. V2 split that differently: ProjectWithPhasesEfforts put only the project-level amounts in its own three members and left the caller to add the phases back on. In V3 the entries sum to the totals, so both readings are available and they agree.
  • Presence writes carry their time zone. V3 stores a presence as a real UTC instant plus the day it counts on and the zone that day was read in, so a presence no longer means something different to a reader in another zone. addResourcePresence therefore asks for day and, optionally, timezone rather than inferring both from whoever is calling, and clockResourcePresence derives the day from the stamp and that same optional zone. Both default to the calling user's zone, so a client that only ever calls in one zone can keep ignoring the parameter.
  • Presences are a V3 object family now. A presence is addressed under its resource (Resource/:resourceId/Presence/:presenceId) and has the usual read, modify, delete, Timeline and Version/:versionId endpoints. The V2 "give me the whole period" read splits the way the rest of V3 does: getAllResourcePresencesByPeriod answers references, getResourcePresences reads them by ID in bulk with one status per element, and the new getAllResourcePresences lists a resource's presences over an optional day range. The two list reads leave out what the caller may not read rather than failing; naming a presence is what gets it reported as forbidden.
  • The summaries read whole periods. getResourcePresenceDailySummary answers one entry per day of the range, getResourcePresenceRangeSummary the same range totalled into one. Both take the same required beg/end range with an exclusive end, and both are refused whole when the caller may not read one of the actuals periods the range touches: a summary over the days that were left would answer a target and a presence total that are wrong for the range that was asked for.
  • The two balances are one endpoint. getResourcePresenceBalance answers the time balance and the per absence type balances together, where V2 needed getTimeBalance and getAbsencesBalance separately. Neither figure covers the year you ask for on its own: both are cumulated from 2000 up to and including it, which V2 said out loud for the time balance and left the caller to know for the absences. The year moves from the path into a required parameter and is named in the answer; the time balance sits in time; and each entry of absences names its absenceTypeId and holds what the time corrections booked on that type credited minus the absences taken on it, so it is negative until an entitlement is booked. Every absence type is reported, including the ones the resource has neither been credited nor been absent on.
  • Project time actuals are a V3 object family now. A booking is addressed under the resource that made it (Resource/:resourceId/ProjectTimeActual/:resourceProjectTimeActualId) and has the usual read, modify, delete, Timeline and Version/:versionId endpoints, plus addResourceProjectTimeActual, which mints the ID rather than letting the caller choose it. The list reads split the way the rest of V3 does: getAllResourceProjectTimeActualsByPeriod answers references for one actuals period, getAllResourceProjectTimeActuals a resource's bookings over an optional day range, getProjectResourcesProjectTimeActuals the same records seen from a project, and getResourceProjectTimeActuals reads them by ID in bulk with one status per element. The references carry the whole identity tuple — day, resourceId, projectId and workItemId — so the entries of a list can be told apart without a second call. As with the presences, the list reads leave out what the caller may not read rather than failing; naming a booking is what gets it reported as forbidden.
  • A booking's day, resource and project are fixed when it is added. putResourceProjectTimeActual modifies workItemId, duration and comment and refuses a body that disagrees with the stored day, resource or project, rather than writing part of it. It does not create either: a body under an unknown ID is a 404, and addResourceProjectTimeActual is the way in. V2's putResourceProjectTimeActual created under a caller-chosen ID and let the day and the project be restated on every write.
  • The presence and the actuals of the V2 allocation slots are ACT time slots. getResourcePresenceSlots and getResourceTimeActualSlotsByProject answer what V2's alc.getMacroAllocationSlots reported as presence and as the forecasts' actual, in time slots, with bulk siblings and single-slot lookups. Like the presence summaries they need READ on every actuals period the span touches. The family is described in the ALC section.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
getActualsPeriod GET ActualsPeriod/:periodId getActualsPeriod GET ActualsPeriod/:periodId
getActualsPeriodByDay GET ActualsPeriod getActualsPeriodByDay GET ActualsPeriod
getActualsPeriods GET ActualsPeriods getAllActualsPeriods GET ActualsPeriods
getResourceActualsPeriod GET Resource/:resourceId/ActualsPeriod/:periodId getResourceActualsPeriod GET Resource/:resourceId/ActualsPeriod/:periodId
getResourceActualsPeriodByDay GET Resource/:resourceId/ActualsPeriod getResourceActualsPeriodByDay GET Resource/:resourceId/ActualsPeriod
delResourcePresence DELETE Resource/:resourceId/Presence/:presenceId delResourcePresence DELETE Resource/:resourceId/Presence/:resourcePresenceId
getResourceActualsPeriods GET Resource/:resourceId/ActualsPeriods getAllResourceActualsPeriods GET Resource/:resourceId/ActualsPeriods
delResourceProjectTimeActual DELETE Resource/:resourceId/ProjectTimeActual/:ptaId delResourceProjectTimeActual DELETE Resource/:resourceId/ProjectTimeActual/:resourceProjectTimeActualId

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
addResourcePresence POST Resource/:resourceId/Presence/Add addResourcePresence POST Resource/:resourceId/Presences/Add The body is gone: the values are parameters, and the presence's day and its timezone are now stated explicitly instead of being read out of the caller's own time zone
clockResourcePresence POST Resource/:resourceId/Presence/Clock/:time clockResourcePresence POST Resource/:resourceId/Presences/Clock time moves from the path into an optional parameter and defaults to now; an optional timezone decides the day the stamp counts on; the answer is a reference to the affected presence, 201 when the stamp opened one and 200 otherwise
delResourceTimeCorrection DELETE Resource/:resourceId/TimeCorrection/:type/:date delResourceTimeCorrection DELETE Resource/:resourceId/TimeCorrection/:correctionId Addresses the correction by its ID instead of type and date; find it with getAllResourceTimeCorrections, whose references carry date and correction type
getDaySummary GET Resource/:resourceId/DaySummary getResourcePresenceDailySummary GET Resource/:resourceId/Presences/Summary/Daily Both days of the range are required now, and every entry names the day it stands for instead of leaving the caller to count them out; the whole range has to be readable, so a day in an actuals period closed to the caller is a 403 rather than a gap
getMidMonthSummary GET Resource/:resourceId/MidMonthSummary getResourcePresenceRangeSummary GET Resource/:resourceId/Presences/Summary/Range Totals any day range rather than the month up to a day: pass the first day of the month as beg and the day after the target one as end. The answer repeats the range it stands for, and its absences are merged per type - V2 listed the same type once per day it was booked on
getTimeBalance GET Resource/:resourceId/TimeBalance/:year getResourcePresenceBalance GET Resource/:resourceId/Presences/Summary/Balance Merged with getAbsencesBalance into one call: the answer carries the time balance in time and the per absence type balances in absences. year moves from the path into a required parameter, and the resource must exist and be readable - V2 answered a zero balance for any ID
getAbsencesBalance GET Resource/:resourceId/AbsenceBalance/:year getResourcePresenceBalance GET Resource/:resourceId/Presences/Summary/Balance Merged with getTimeBalance into one call: the per absence type balances sit in absences, each naming its absenceTypeId, and the answer states the year they are cumulated up to - V2 left the caller to remember it
getResourcePresences GET Resource/:resourceId/ActualsPeriod/:periodId/Presences getAllResourcePresencesByPeriod GET Resource/:resourceId/ActualsPeriod/:periodId/Presences Answers references rather than whole presences; read them with the new bulk getResourcePresences (GET Resource/:resourceId/Presences/:presenceIds), which reports one status per element
putResourcePresence PUT Resource/:resourceId/Presence/:presenceId putResourcePresence PUT Resource/:resourceId/Presence/:resourcePresenceId Request body changed; the day a presence counts on and the timezone it was recorded in are fixed when it is added and a change to either is refused
getProjectEfforts GET Resource/:resourceId/Efforts/Project/:projectid getResourceProjectEfforts GET Resource/:resourceId/Project/:projectId/Efforts id renamed to projectId, resourceId added; optional beg/end day range added
getProjectsEfforts GET Resource/:resourceId/Efforts/Projects/:projectids getResourceProjectsEfforts GET Resource/:resourceId/Projects/:projectIds/Efforts Same body change and the same optional day range; projects the caller may not read are no longer dropped silently but reported per element
getProjectWithPhasesEfforts GET Resource/:resourceId/Efforts/ProjectWithPhases/:projectid getResourceProjectWithWorkItemEfforts GET Resource/:resourceId/Project/:projectId/Efforts/WithWorkItems phases becomes workItems and names a workItemId; the three top-level amounts are the totals over the whole project now, not the project-level part alone, and what was allocated on the project itself appears as the entry with no workItemId; optional beg/end day range added
getProjectsWithPhasesEfforts GET Resource/:resourceId/Efforts/ProjectsWithPhases/:projectids getResourceProjectsWithWorkItemEfforts GET Resource/:resourceId/Projects/:projectIds/Efforts/WithWorkItems Same body change and the same optional day range; projects the caller may not read are no longer dropped silently but reported per element
getProjectTimeActuals GET Project/:projectId/ProjectTimeActuals getProjectResourcesProjectTimeActuals GET Project/:projectId/Resources/:resourceIds/ProjectTimeActuals The resources are named rather than derived from the project, from/to become beg/end, and the answer is references rather than whole records — read them with getResourceProjectTimeActuals
getResourceProjectTimeActual GET Resource/:resourceId/ProjectTimeActual/:ptaId getResourceProjectTimeActual GET Resource/:resourceId/ProjectTimeActual/:resourceProjectTimeActualId Response body changed: the V3 contract carries the version and audit information of VersionedReference, and createdBy/modifiedBy are references rather than items
getResourceProjectTimeActuals GET Resource/:resourceId/ActualsPeriod/:periodId/ProjectTimeActuals getAllResourceProjectTimeActualsByPeriod GET Resource/:resourceId/ActualsPeriod/:periodId/ProjectTimeActuals Answers references rather than whole records; read them with the new bulk getResourceProjectTimeActuals (GET Resource/:resourceId/ProjectTimeActuals/:resourceProjectTimeActualIds), which reports one status per element
putResourceProjectTimeActual PUT Resource/:resourceId/ProjectTimeActual/:ptaId putResourceProjectTimeActual PUT Resource/:resourceId/ProjectTimeActual/:resourceProjectTimeActualId Request body changed, and the endpoint no longer creates: day, resourceId and projectId are fixed when the booking is added and a change to any of them is refused — use addResourceProjectTimeActual to create
getResourceTimeCorrections GET Resource/:resourceId/TimeCorrections getAllResourceTimeCorrections GET Resource/:resourceId/TimeCorrections Returns references; read them with getResourceTimeCorrections
putResourceTimeCorrection PUT Resource/:resourceId/TimeCorrection/:type/:date putResourceTimeCorrection PUT Resource/:resourceId/TimeCorrection/:correctionId Addresses the correction by its ID instead of type and date; find it with getAllResourceTimeCorrections, whose references carry date and correction type. Takes the V3 ResourceTimeCorrection as its body

Retired — no V3 successor; the endpoint disappears with API V2.

V2 endpoint V2 path Note
delResourceProjectPhaseTime DELETE Resource/:resourceId/ProjectTime/:day/:projectId/:workItemId This API call is deprecated as it does not support more than one project time per project, task and day, will be discontinued at end of 2028
delResourceProjectTime DELETE Resource/:resourceId/ProjectTime/:day/:projectId This API call is deprecated as it does not support more than one project time per project and day, will be discontinued at end of 2028
getResourceProjectPhaseTime GET Resource/:resourceId/ProjectTime/:day/:projectId/:workItemId This API call is deprecated as it does not support more than one project time per project, task and day, will be discontinued at end of 2028
getResourceProjectTime GET Resource/:resourceId/ProjectTime/:day/:projectId This API call is deprecated as it does not support more than one project time per project and day, will be discontinued at end of 2028
getResourceProjectTimes GET Resource/:resourceId/ActualsPeriod/:periodId/ProjectTimes This API call is deprecated as it does not support more than one project time per project and day, will be discontinued at end of 2028
putResourceProjectPhaseTime PUT Resource/:resourceId/ProjectTime/:day/:projectId/:workItemId This API call is deprecated as it does not support more than one project time per project, task and day, will be discontinued at end of 2028
putResourceProjectTime PUT Resource/:resourceId/ProjectTime/:day/:projectId This API call is deprecated as it does not support more than one project time per project and day, will be discontinued at end of 2028

New in V3 — no V2 equivalent.

addResourceProjectTimeActual, getActualsPeriods, getAllResourcePresences, getAllResourceProjectTimeActuals, getProjectResourcesEfforts, getResourceActualsPeriods, getResourcePresence, getResourcePresenceTimeline, getResourcePresenceVersion, getResourceProjectTimeActualTimeline, getResourceProjectTimeActualVersion, getResourceTimeCorrection, getResourceTimeCorrectionTimeline, getResourceTimeCorrectionVersion, getResourcesPresenceSlot, getResourcesTimeActualSlotByProject

ALC — Allocations and capacity planning

Allocation and capacity planning is fully in V3. Six families make it up: the absence types, the involvement endpoints, which answer which projects a resource works on and which resources work on a project, the day-by-day planning matrix, which V3 unifies into the resource shift plan, the macro allocations, which V3 calls resource allocations, the allocation slots, which V3 splits into the time slot family across RES, ALC and ACT, and the allocation workflow.

  • Absence types are a V3 object family, addressed in their translated form only. getAbsenceTypes splits the V2 "give me everything" call into getAllAbsenceTypes (references, sortable) and getAbsenceTypes (bulk read by ID); getAbsenceTypesTranslated is new. The untranslated writes are gone: putAbsenceType is retired, and delAbsenceType redirects to delAbsenceTypeTranslated — there is only one delete.
  • AbsenceSlots, MicroAllocationSlots and PlanningSlots become one matrix. All three V2 families described the same grid of days and resource shifts, and differed only in how much of it they filled in. V3 has one read, getResourceShiftPlan, which always reports the whole grid — the absences and the reservations (the new name for micro allocations) — and one write, updateResourceShiftPlan. The body of either is the matrix itself, one row per resource shift: { begin, rows: [ { shiftId, slots: [ { absence: { ref, remark }, reservation: { ref, remark } } ] } ] }. A row names its shift by ID only, so read the shift itself with res.getResourceShifts; every row holds one slot per day, the first one for begin. The statistics over the matrix are a read of their own, getResourceShiftPlanStatistics (see below).
  • The write takes one update mode per dimension. absences and reservations both default to UPDATE_OR_DELETE, which makes that dimension match the body exactly. UPDATE_ONLY writes creations and changes but keeps an entry the body leaves empty, and KEEP does not touch the dimension at all. The two V2 writes are therefore updateResourceShiftPlan(…, absences=UPDATE_OR_DELETE, reservations=KEEP) and its mirror image — and writing both dimensions in one call, which V2 could not do, is the default.
  • Every slot reports its own status. X-Detail-Codes holds one code per slot of the request, row by row and within a row day by day, and 400 marks every slot an update mode refused to write: under KEEP each slot the request would have changed, under UPDATE_ONLY each slot it would have emptied. The call still answers 200 as long as one slot was written — see section 5.3.
  • Permissions follow the dimension that is really written. Reading the matrix needs the permission to read the actuals of the resource. Writing the absences needs PLAN on the resource and UPDATE on every actuals period the matrix spans; writing the reservations needs RESERVE. A mode set to KEEP demands neither, so a caller who may only reserve can still send the whole matrix with absences=KEEP.
  • Absence and reservation share one shape. Both are a ResourceShiftPlanEntry, a PF.Reference plus a remark: the ref of an absence names its absence type (V2 absenceType), the ref of a reservation its project work item (V2 phaseId, section 6); any other type is answered with 422. A slot may hold both, since an absence need not keep the resource from working. The remark of a reservation is new; a V2 write, which knows none, keeps the stored remark of every reservation whose work item it leaves unchanged. The matrix names nothing but these references: V2's phaseName, projectId, projectName, scenarioId, scenarioName, absenceName and absenceColor have no V3 counterpart, since the names disclosed objects the caller need not be allowed to see - read them from the objects themselves, e.g. alc.getAbsenceTypes. Nor does a slot report V2's presence, model (the worktime model with its holiday) and description any longer; getResourceShiftPlanStatistics counts the available and the bank holiday slots.
  • What changed in a slot is a list of its own. getResourceShiftPlanAbsenceChanges and getResourceShiftPlanReservationChanges (GET Resource/:resourceId/ShiftPlan/Absences/Changes and …/Reservations/Changes) answer the changes to one dimension of the shift plan on the given days, newest first: the slot (day, shiftId), the entry before and after the change (previous, current, each absent for an empty slot), and modifiedAt and modifiedBy; from, until and limit narrow them down. They replace the absencesTimeline and reservationsTimeline of V2's getMacroAllocationHistory, from the same records: slots are not versioned, so a slot changed in place shows only its latest state, as in V2.
  • The statistics count slots, grouped by reference. getPlanningSlotStats is deprecated in favour of getResourceShiftPlanStatistics, GET Resource/:resourceId/ShiftPlan/Statistics. Instead of the V2 arrays absences and projects of { id, hint, slotcount } it reports one array slotCounts of { ref, count }, where ref is a PF.Reference to an absence type or a project — by type and ID, the V2 hint (the name) is gone. available and unavailable become the plain counts availableSlots and unavailableSlots, and holidaySlots counts the slots on a bank holiday, which V2 did not report. A slot now counts as available exactly when the shift plan reports it as present, so the resource's own shift plan is honoured. Unlike the matrix, the statistics are not bounded by pqforce.dayrange.max.
  • Clearing the reservations of one work item is an ALC call now. pm.delMicroAllocations redirects to unassignResourceShiftPlanByProjectWorkItem, POST Resource/:resourceId/ShiftPlan/ByProjectWorkItem/:projectWorkItemId/Delete, which deletes every reservation that work item holds in the plan of that resource, over the whole time the plan spans, and touches no absence. It needs RESERVE on the resource, like every other write of reservations, and UPDATE on the work item; the V2 call asked for that same UPDATE, but for PLAN on the resource.
  • So is writing them. pm.assignWorkItemShiftPlan redirects to assignResourceShiftPlanByProjectWorkItem, POST Resource/:resourceId/ShiftPlan/ByProjectWorkItem/:projectWorkItemId/Assign, the exact counterpart of the delete above - same path, Assign instead of Delete, same two permissions, and the same move from PLAN to RESERVE on the resource. It takes the pattern of days and shifts from the shift plan of the work item (pm.getProjectWorkItemShiftPlan) and lays it out from the work item's start onwards, one reservation per planned slot. Only the URL really differs: the mandatory res became the path segment :resourceId, beside the :projectWorkItemId that V2 carried in the path as :workItemId. force keeps its name and its meaning - without it a slot another reservation already holds is left alone - and a work item without a start is still answered 400.
  • Macro allocations are resource allocations, an ALC object family. The seven V2 endpoints were part of PM; V3 addresses a ResourceAllocation by its own ID alone - getResourceAllocation(s), putResourceAllocation, delResourceAllocation and getResourceAllocationIdentifications at ALC/ResourceAllocation/:resourceAllocationId
    • because allocations are going to belong to the project rather than to one of its work items. The object therefore names what it belongs to in a read-only parent reference (today always a project work item, V2 workItemId) instead of V2's projectId, scenarioId and workItemId; resourceType is gone as well. Its type is still MacroAllocation. addResourceAllocation, POST ALC/ProjectWorkItem/:projectWorkItemId/ResourceAllocations/Add, creates one with resourceId, amount, amountUnit and amountType; a resource can be allocated to a work item only once, so a second one is answered 400, where V2 silently overwrote the existing allocation. putResourceAllocation only updates, and the work item and the resource it ignores. The lists are references with the work item as parent, by work item, by scenario and - new - by project (getAllResourceAllocationsOfProject, its active scenario or the single inactive one). The enumerations are spelled the V3 way: AllocationWorkflowState is New, Changed, Requested, Rejected, Approved, and ResourceAllocationAmountType is Absolute, OfCapacity, OfAvailability, OfFte and OfProjectWorkItem (V2 OF_PHASE). The history follows the general pattern (section 5.6): getResourceAllocationTimeline and getResourceAllocationVersion. A version reads the allocation as it was then, but without effectiveDuration, which only today's planning yields. What changed across all allocations of one resource is getResourceAllocationChangesOfResource (GET Resource/:resourceId/ResourceAllocations/Changes?beg&end), newest first: each change names the allocation with its work item as parent, the version it wrote, the previousVersion and whether it deleted the allocation - read either version with getResourceAllocationVersion. It replaces the allocationTimeline of V2's getMacroAllocationHistory. A project counts with its current scenario, and only while the resource is still involved in it. The V2 endpoints are listed in the PM section.
  • The allocation workflow moves an allocation like a lifecycle. requestResourceAllocation, approveResourceAllocation and rejectResourceAllocation (POST ResourceAllocation/:resourceAllocationId/Request, …/Approve, …/Reject) answer 200 without a body. A remark is not a URL parameter any more: each has a …WithRemark sibling (…/Request/WithRemark and so on) taking a lcy.LifecycleUpgradeRemark as body, as lcy.upgradeObject does. A remark without a move is addResourceAllocationRemark (…/Remark/Add, V2 commentResourceAllocation), which answers 201. A move the workflow does not allow from the current state is answered 400, and so is every write while the allocation workflow is not enabled; the history and the new getResourceAllocationWorkflowTransitions - the states the caller may move the allocation to now - answer 404 then. Requesting needs ALLOCATE on the work item, approving and rejecting ALLOCATE on the resource, a remark either of the two; autoApprove (V2 autoapprove) still approves a request at once if the caller may.
  • The allocation slots become a family of time slots, one time value per endpoint. A V2 Workload answered availability, presence and the forecasts of every group in one array of slots. V3 answers each value on its own, in the module it belongs to: res.getResourceAvailabilitySlots, getResourcePlannedTimeSlotsByProject, getResourceScenarioPlannedTimeSlots, act.getResourcePresenceSlots and act.getResourceTimeActualSlotsByProject. Each has a bulk sibling for several resources at Resources/:resourceIds/… (V2 getMacroAllocationSlotsByResource), and a single-slot lookup that answers one slot over exactly the requested span — …Slot instead of …Slots, the successor of getMacroAllocationSlot. All of them cut a span into the very same slots, so their answers can be laid over one another, and a bulk request answers every resource in the same slots. The Workload envelope itself is gone: an answer is the array of slots.
  • The planned time by work item is a single slot too, for one project with its current scenario, getResourceProjectPlannedTimeSlotByProjectWorkItem (V2 getProjectPhaseAllocationsByResource), and for one scenario, active or not, getResourceScenarioPlannedTimeSlotByProjectWorkItem (V2 getScenarioPhaseAllocationsByResource). Their bulk siblings take the resources from you — find them with getInvolvedResources (V2 getProjectPhaseAllocations, getScenarioPhaseAllocations). The time on a work item the caller may not IDENTIFY is answered under the reference of the project or the scenario itself.
  • A slot names its own time span. A V2 slot was a position in an array the client had to map back to beg and zoom. A V3 PF.TimeSlot carries beg, end (exclusive), unit (a PF.CalendarGranularity) and complete — false where the requested span cuts the slot — plus capacity, the scale of the slot as the resource's worktime model defines it; a time may exceed it. zoom is optional: given, every slot has that unit; omitted, the server chooses the slots, and they may differ in width — read beg and end instead of assuming one width. A single slot reports the smallest calendar unit one instance of which contains the whole span, e.g. Week for Tuesday to Friday. With zoom=Day the days of all requested resources together are bounded by pqforce.dayrange.max (section 5.13), and the span has to lie in the planning range, as in V2.
  • Grouped times are references, and always by project. groupBy is gone, and with it the grouping by lifecycle state; so is the projectId filter. A PF.GroupedDurationSlot holds a times array of PF.ReferenceDuration, a project reference with its time; a project without time in a slot is left out, so there is no grouping array to index into any more. Every project counts, whatever its state, with its active scenario or, if it has none, its single inactive one — in the planned time and in the actuals alike. V2 left out the planned time of projects that were neither active nor in planning; in V3 you pick the projects of interest from the answer yourself. Time on a project the caller may not IDENTIFY goes to an entry of the type BaseLoad, whose ID is derived from the resource's ID and stays the same from one call to the next; in the planned time it also holds the base load of the resource's worktime model, in the actuals it never does.
  • What else changed. The availability is the working time without absences before the base load is taken off; V2's availability was after it. The planned time of one scenario, active or not, is a call of its own (V2 scenarioId, getScenarioAllocationSlots, and getProjectAllocationSlots, for which you pass the project's active scenario). The presence is the reported presence only. expectedPresence, the forecast values actual, expectedActual and remaining, deviationFromOptimum with its fewerWorkWeight, fte, ftePercent, maxFtePercent, maxCapacity and the actualsReportedUntil / presenceReportedUntil days have no V3 counterpart. The presence and the actuals slots total whole spans, like the presence summaries, and so need READ on every actuals period the span touches.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
delAbsenceType DELETE AbsenceType/:absenceTypeId delAbsenceTypeTranslated DELETE AbsenceType/:absenceTypeId/WithTranslations
delAbsenceTypeTranslated DELETE AbsenceType/:absenceTypeId/WithTranslations delAbsenceTypeTranslated DELETE AbsenceType/:absenceTypeId/WithTranslations
getUnoptimizedResourcesByParent GET UnoptimizedResources/ByParent/:resourceId getUnoptimizedResourcesByParent GET UnoptimizedResources/ByParent/:resourceId

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
approveResourceAllocation POST ResourceAllocation/:allocationId/Approve approveResourceAllocation POST ResourceAllocation/:resourceAllocationId/Approve Without a remark; the remark is no longer a URL parameter but the body of approveResourceAllocationWithRemark (…/WithRemark), a lcy.LifecycleUpgradeRemark; a move the workflow does not allow is answered 400; 400 while the allocation workflow is not enabled
commentResourceAllocation POST ResourceAllocation/:allocationId/Comment addResourceAllocationRemark POST ResourceAllocation/:resourceAllocationId/Remark/Add The remark is the body, a lcy.LifecycleUpgradeRemark, and required (422); answers 201; 400 while the allocation workflow is not enabled
getAbsenceSlots GET AbsenceSlots/ByResource/:resourceId getResourceShiftPlan GET Resource/:resourceId/ShiftPlan Reports the whole matrix, not only its absences; body restructured and members renamed
getAbsenceType GET AbsenceType/:absenceTypeId getAbsenceType GET AbsenceType/:absenceTypeId Response body changed
getAbsenceTypes GET AbsenceTypes getAllAbsenceTypes GET AbsenceTypes Reports references and takes a sort; read the objects with getAbsenceTypes(:absenceTypeIds)
getAbsenceTypeTranslated GET AbsenceType/:absenceTypeId/WithTranslations getAbsenceTypeTranslated GET AbsenceType/:absenceTypeId/WithTranslations Response body changed
getAvailabilitySlot GET AvailabilitySlot/ByResource/:resourceId res.getResourceAvailabilitySlot GET RES/Resource/:resourceId/AvailabilitySlot The availability is before the base load is taken off; fte and ftePercent are not answered; the span has to lie in the planning range
getInvolvedProjects GET Resource/:resourceId/ProjectsInvolved getInvolvedProjects GET Resource/:resourceId/InvolvedProjects Reports PF.Reference instead of PF.Item, accepts an optional sort, and the array only contains what the caller may IDENTIFY
getInvolvedResources GET Project/:projectId/ResourcesInvolved getInvolvedResources GET Project/:projectId/InvolvedResources Reports PF.Reference instead of PF.Item, accepts an optional sort, and the array only contains what the caller may IDENTIFY
getMacroAllocationHistory GET MacroAllocationHistory/ByResource/:resourceId getResourceAllocationChangesOfResource GET Resource/:resourceId/ResourceAllocations/Changes One change list per timeline: the allocations here, the absences and reservations in getResourceShiftPlanAbsenceChanges and getResourceShiftPlanReservationChanges, the active scenario and the work items in pm.getActiveScenarioTimelineOfProject and pm.getProjectWorkItemTimeline; objects are references, an allocation's states are read with getResourceAllocationVersion
getMacroAllocationSlot GET MacroAllocationSlot/ByResource/:resourceId getResourcePlannedTimeSlotByProject GET Resource/:resourceId/PlannedTimeSlot/ByProject One time value per endpoint: availability, presence and actuals are res.getResourceAvailabilitySlot, act.getResourcePresenceSlot and act.getResourceTimeActualSlotByProject; the slot names its span and unit; always by project, groupBy, projectId, scenarioId and fewerWorkWeight are gone
getMacroAllocationSlots GET MacroAllocationSlots/ByResource/:resourceId getResourcePlannedTimeSlotsByProject GET Resource/:resourceId/PlannedTimeSlots/ByProject One time value per endpoint: availability, presence and actuals are res.getResourceAvailabilitySlots, act.getResourcePresenceSlots and act.getResourceTimeActualSlotsByProject; answers the array of slots, each naming its span and unit; zoom is optional; always by project, groupBy, projectId, scenarioId and fewerWorkWeight are gone
getMacroAllocationSlotsByResource GET MacroAllocationSlots/ByResource getResourcesPlannedTimeSlotsByProject GET Resources/:resourceIds/PlannedTimeSlots/ByProject As getMacroAllocationSlots, one row of slots per requested resource; the bulk siblings res.getResourcesAvailabilitySlots, act.getResourcesPresenceSlots and act.getResourcesTimeActualSlotsByProject answer the other values
getMicroAllocationSlots GET MicroAllocationSlots/ByResource/:resourceId getResourceShiftPlan GET Resource/:resourceId/ShiftPlan Reports the whole matrix, not only its reservations; body restructured and members renamed
getPlanningSlots GET PlanningSlots/ByResource/:resourceId getResourceShiftPlan GET Resource/:resourceId/ShiftPlan Body restructured and members renamed; the shift is named by ID, not embedded
getPlanningSlotStats GET PlanningSlotStats/ByResource/:resourceId getResourceShiftPlanStatistics GET Resource/:resourceId/ShiftPlan/Statistics One array of references and counts instead of two arrays with names; adds the bank holiday slots
getProjectAllocationsByResource GET ProjectAllocations/ByResource/:resourceId getResourcePlannedTimeSlotByProject GET Resource/:resourceId/PlannedTimeSlot/ByProject One single slot of time slot references per endpoint: the actuals are act.getResourceTimeActualSlotByProject and the availability res.getResourceAvailabilitySlot, the latter before the base load is taken off; expectedPending and remaining are not answered, includeInactive is gone (every project counts), a project is a reference without name and state
getProjectAndPhaseAllocationsByResource GET ProjectAndPhaseAllocations/ByResource/:resourceId getResourcePlannedTimeSlotByProject GET Resource/:resourceId/PlannedTimeSlot/ByProject No ready-made structure: the projects come as references with their planned time, the work items of one project from getResourceProjectPlannedTimeSlotByProjectWorkItem, their names and dates from pm.getProjectWorkItems; only what has time planned within the span is answered, and every project counts, whatever its state
getProjectAllocationSlots GET MacroAllocationSlots/ByResource/:resourceId/ForProject/:projectId getResourceScenarioPlannedTimeSlots GET Resource/:resourceId/ProjectScenario/:projectScenarioId/PlannedTimeSlots Addressed by the project's active scenario instead of the project; answers the planned time only, availability and presence are res.getResourceAvailabilitySlots and act.getResourcePresenceSlots
getProjectPhaseAllocations GET Project/:projectId/PhaseAllocations getResourcesProjectPlannedTimeSlotByProjectWorkItem GET Resources/:resourceIds/Project/:projectId/PlannedTimeSlot/ByProjectWorkItem Name the resources yourself, e.g. from getInvolvedResources; one single slot of the planned time per work item reference per resource; actuals and availability are act.getResourcesTimeActualSlotByProject and res.getResourcesAvailabilitySlot; actuals, expectedPending and remaining per work item are not answered
getProjectPhaseAllocationsByResource GET Project/:projectId/PhaseAllocations/ByResource/:resourceId getResourceProjectPlannedTimeSlotByProjectWorkItem GET Resource/:resourceId/Project/:projectId/PlannedTimeSlot/ByProjectWorkItem One single slot of the planned time per work item reference; the project's actuals are act.getResourceTimeActualSlotByProject; actuals, expectedPending and remaining per work item are not answered
getScenarioPhaseAllocations GET ProjectScenario/:scenarioId/PhaseAllocations getResourcesScenarioPlannedTimeSlotByProjectWorkItem GET Resources/:resourceIds/ProjectScenario/:projectScenarioId/PlannedTimeSlot/ByProjectWorkItem Name the resources yourself, e.g. from getInvolvedResources; one single slot of the scenario's planned time per work item reference per resource; actuals, expectedPending and remaining per work item are not answered
getScenarioPhaseAllocationsByResource GET ProjectScenario/:scenarioId/PhaseAllocations/ByResource/:resourceId getResourceScenarioPlannedTimeSlotByProjectWorkItem GET Resource/:resourceId/ProjectScenario/:projectScenarioId/PlannedTimeSlot/ByProjectWorkItem One single slot of the scenario's planned time per work item reference; the project's actuals are act.getResourceTimeActualSlotByProject; actuals, expectedPending and remaining per work item are not answered
getResourceAllocationWorkflowHistory GET ResourceAllocation/:allocationId/Workflow/History getResourceAllocationWorkflowHistory GET ResourceAllocation/:resourceAllocationId/Workflow/History Entries name their user by reference only (initiatedBy) and carry their version; 404 while the allocation workflow is not enabled
getScenarioAllocationSlots GET MacroAllocationSlots/ByResource/:resourceId/ForScenario/:scenarioId getResourceScenarioPlannedTimeSlots GET Resource/:resourceId/ProjectScenario/:projectScenarioId/PlannedTimeSlots Answers the planned time of the scenario only, ungrouped; availability and presence are res.getResourceAvailabilitySlots and act.getResourcePresenceSlots; fewerWorkWeight is gone
putAbsenceTypeTranslated PUT AbsenceType/:absenceTypeId/WithTranslations putAbsenceTypeTranslated PUT AbsenceType/:absenceTypeId/WithTranslations Request body changed
rejectResourceAllocation POST ResourceAllocation/:allocationId/Reject rejectResourceAllocation POST ResourceAllocation/:resourceAllocationId/Reject Without a remark; the remark is no longer a URL parameter but the body of rejectResourceAllocationWithRemark (…/WithRemark), a lcy.LifecycleUpgradeRemark; a move the workflow does not allow is answered 400; 400 while the allocation workflow is not enabled
requestResourceAllocation POST ResourceAllocation/:allocationId/Request requestResourceAllocation POST ResourceAllocation/:resourceAllocationId/Request Without a remark; the remark is no longer a URL parameter but the body of requestResourceAllocationWithRemark (…/WithRemark), a lcy.LifecycleUpgradeRemark; autoapprove is spelled autoApprove; a move the workflow does not allow is answered 400; 400 while the allocation workflow is not enabled
updateAbsenceSlots POST AbsenceSlots/ByResource/:resourceId/Update updateResourceShiftPlan POST Resource/:resourceId/ShiftPlan/Update One write for both dimensions; pass reservations=KEEP to keep the V2 scope
updateMicroAllocationSlots POST MicroAllocationSlots/ByResource/:resourceId/Update updateResourceShiftPlan POST Resource/:resourceId/ShiftPlan/Update One write for both dimensions; pass absences=KEEP to keep the V2 scope

Retired — no V3 successor; the endpoint disappears with API V2.

V2 endpoint V2 path Note
putAbsenceType PUT AbsenceType/:absenceTypeId Use version with translations instead, will be discontinued at end of 2028

New in V3 — no V2 equivalent.

approveResourceAllocationWithRemark, getAbsenceTypesTranslated, getAllResourceAllocationsOfProject, getResourceAllocationIdentifications, getResourceAllocationChangesOfResource, getResourceAllocationTimeline, getResourceAllocationVersion, getResourceAllocationWorkflowTransitions, getResourceShiftPlanAbsenceChanges, getResourceShiftPlanReservationChanges, getResourceScenarioPlannedTimeSlot, getResourcesPlannedTimeSlotByProject, getResourcesProjectPlannedTimeSlotByProjectWorkItem, getResourcesScenarioPlannedTimeSlot, getResourcesScenarioPlannedTimeSlotByProjectWorkItem, rejectResourceAllocationWithRemark, requestResourceAllocationWithRemark

ATM — Automation (JavaScript sources and triggers)

Fully migrated, and almost entirely by redirect — the cheapest module in this guide. The only rename is triggerJavaScriptSource → triggerJavaScriptSandbox (JavaScript only on both sides), and the source endpoints gained a reference list and historic versions.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
addJavaScriptChangeTrigger POST JavaScript/Source/:sourceId/ChangeTriggers/Add addJavaScriptChangeTrigger POST JavaScript/Sandbox/:sandboxId/ChangeTriggers/Add
clearJavaScriptConsole POST JavaScript/Console/Clear clearJavaScriptConsole POST JavaScript/Console/Clear
clearJavaScriptConsoleOfSandbox POST JavaScript/Console/:sandboxId/Clear clearJavaScriptConsoleOfSandbox POST JavaScript/Console/:sandboxId/Clear
clearJavaScriptSandboxPerformance POST JavaScript/Performance/:sandboxId/Clear clearJavaScriptSandboxPerformance POST JavaScript/Performance/:sandboxId/Clear
delJavaScriptChangeTrigger DELETE JavaScript/Source/:sourceId/ChangeTrigger/:triggerId delJavaScriptChangeTrigger DELETE JavaScript/Sandbox/:sandboxId/ChangeTrigger/:triggerId
delJavaScriptSource DELETE JavaScript/Source/:sourceId delJavaScriptSource DELETE JavaScript/Source/:javaScriptSourceId
getJavaScriptChangeTrigger GET JavaScript/Source/:sourceId/ChangeTrigger/:triggerId getJavaScriptChangeTrigger GET JavaScript/Sandbox/:sandboxId/ChangeTrigger/:triggerId
getJavaScriptChangeTriggers GET JavaScript/Source/:sourceId/ChangeTriggers getJavaScriptChangeTriggers GET JavaScript/Sandbox/:sandboxId/ChangeTriggers
getJavaScriptConsole GET JavaScript/Console getJavaScriptConsole GET JavaScript/Console
getJavaScriptConsoleOfSandbox GET JavaScript/Console/:sandboxId getJavaScriptConsoleOfSandbox GET JavaScript/Console/:sandboxId
getJavaScriptLibraries GET JavaScript/Libraries getJavaScriptLibraries GET JavaScript/Libraries
getJavaScriptQuota GET JavaScript/Quota getJavaScriptQuota GET JavaScript/Quota
getJavaScriptSandboxPerformance GET JavaScript/Performance/:sandboxId getJavaScriptSandboxPerformance GET JavaScript/Performance/:sandboxId
runJavaScriptSource POST JavaScript/Source/:sourceId/Run runJavaScriptSandbox POST JavaScript/Sandbox/:sandboxId/Run
triggerJavaScriptSource JS only triggerJavaScriptSandbox JS only

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getJavaScriptSource GET JavaScript/Source/:sourceId getJavaScriptSource GET JavaScript/Source/:javaScriptSourceId The version parameter is not available, read a historic version with getJavaScriptSourceVersion using a version ID from getJavaScriptSourceTimeline
getJavaScriptSourceCompileErrors GET JavaScript/Source/:sourceId/CompileErrors getJavaScriptSandboxCompileErrors GET JavaScript/Sandbox/:sandboxId/CompileErrors Names the source sourceId
getJavaScriptSources GET JavaScript/Sources getAllJavaScriptSources GET JavaScript/Sources Returns references; read them with getJavaScriptSources
getJavaScriptSourceTimeline GET JavaScript/Source/:sourceId/Timeline getJavaScriptSourceTimeline GET JavaScript/Source/:javaScriptSourceId/Timeline Takes from, until and limit instead of beg and end
putJavaScriptSource PUT JavaScript/Source/:sourceId putJavaScriptSource PUT JavaScript/Source/:javaScriptSourceId Request body changed

New in V3 — no V2 equivalent.

getJavaScriptSourceVersion

CHT — Charts

Two endpoints, both reimplemented: the chart and its meta model return new bodies. There is no redirect, so rewrite both calls.

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getChart GET Chart/:baseline/:baselinefilter getChart GET Chart/:baselineId/:baselinefilter/?... Response body changed
getChartMetaModel GET MetaModel getChartMetaModel GET MetaModel Response body changed

CLF — Client framework (cockpit widgets, stores, translations)

Mostly redirected. Three things to note:

  • The stores gained reference lists — getGlobalStores, getUserStores and getAllCockpitWidgets — so you can enumerate before reading.
  • sanitizeHtml is retired. In a JavaScript automation use Html.sanitize(…); over HTTP, sanitizing happens on the server for you (section 4).
  • newUuids is restricted to the browser client in V3. In a JavaScript automation use Uuid.generate(); in an integration, generate UUIDs locally — there is no reason to spend a round trip on it.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
calculateScriptedCockpitWidget POST CockpitWidget/:widgetId/:type/:id/Calculate calculateScriptedCockpitWidget POST CockpitWidget/:widgetId/:objectType/:objectId/Calculate
delCockpitWidgetTranslated DELETE CockpitWidget/:widgetId/WithTranslations delCockpitWidgetTranslated DELETE CockpitWidget/:widgetId/WithTranslations
delGlobalStore DELETE GlobalStore/:storeId delGlobalStore DELETE GlobalStore/:storeId
delObjectStore DELETE ObjectStore/:objectType/:objectId/:storeId delObjectStore DELETE ObjectStore/:objectType/:objectId/:storeId
delUserStore DELETE UserStore/:storeId delUserStore DELETE UserStore/:storeId
getGlobalStore GET GlobalStore/:storeId getGlobalStore GET GlobalStore/:storeId
getGlobalStoreIds GET GlobalStores/Ids getGlobalStoreIds GET GlobalStores
getObjectStore GET ObjectStore/:objectType/:objectId/:storeId getObjectStore GET ObjectStore/:objectType/:objectId/:storeId
getObjectStoreIds GET ObjectStores/Ids getObjectStoreIds GET ObjectStores/Ids
getSharedData GET SharedData/:dataId getSharedData GET SharedData/:dataId
getTranslationOverrides GET TranslationOverrides/:lang getTranslationOverrides GET TranslationOverrides/:lang
getUserStore GET UserStore/:storeId getUserStore GET UserStore/:storeId
getUserStoreIds GET UserStores/Ids getUserStoreIds GET UserStores
putGlobalStore PUT GlobalStore/:storeId putGlobalStore PUT GlobalStore/:storeId
putObjectStore PUT ObjectStore/:objectType/:objectId/:storeId putObjectStore PUT ObjectStore/:objectType/:objectId/:storeId
putUserStore PUT UserStore/:storeId putUserStore PUT UserStore/:storeId
reportThirdPartyError POST ThirdPartyError/Report reportError POST Error/Report
shareData POST ShareData shareData POST ShareData

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getCockpitWidget GET CockpitWidget/:widgetId getCockpitWidget GET CockpitWidget/:widgetId Response body changed
getCockpitWidgets GET CockpitWidgets getAllCockpitWidgets GET CockpitWidgets Returns references; read them with getCockpitWidgets
getCockpitWidgetsTranslated GET CockpitWidgets/WithTranslations getAllCockpitWidgets GET CockpitWidgets Returns references; read them with getCockpitWidgetsTranslated
getCockpitWidgetTranslated GET CockpitWidget/:widgetId/WithTranslations getCockpitWidgetTranslated GET CockpitWidget/:widgetId/WithTranslations Response body changed
newUuids GET Uuid/New/:count newUuids GET Uuid/New/:count Interactive clients only; in JavaScript use Uuid.generate(). Response body changed: the array is uuids (V2: newUuids)
putCockpitWidgetTranslated PUT CockpitWidget/:widgetId/WithTranslations putCockpitWidgetTranslated PUT CockpitWidget/:widgetId/WithTranslations Request body changed

Retired — no V3 successor; the endpoint disappears with API V2.

V2 endpoint V2 path Note
sanitizeHtml JS only Use Html.sanitize(...) instead, will be discontinued at end of 2028

New in V3 — no V2 equivalent.

getGlobalStores, getUserStores

CLS — Classification schemes

Five endpoints. The schema getters and the translated variants return new bodies; only putSchemaTranslated redirects.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
delSchemaTranslated DELETE ClassificationSchema/:schemaId/WithTranslations delSchemaTranslated DELETE ClassificationSchema/:schemaId/WithTranslations

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getSchema GET ClassificationSchema/:schemaId getSchema GET ClassificationSchema/:schemaId Response body changed
getSchemasForType GET ObjectType/:typeId/ClassificationSchemas getSchemasForType GET ObjectType/:typeId/ClassificationSchemas Expects the V3 object type (ProjectWorkItem instead of Phase)
getSchemaTranslated GET ClassificationSchema/:schemaId/WithTranslations getSchemaTranslated GET ClassificationSchema/:schemaId/WithTranslations Response body changed
putSchemaTranslated PUT ClassificationSchema/:schemaId/WithTranslations putSchemaTranslated PUT ClassificationSchema/:schemaId/WithTranslations Request body changed

DOC — Legacy document exports

The whole module is retired. See section 8.1 for the EXP replacement.

Retired — no V3 successor; the endpoint disappears with API V2.

V2 endpoint V2 path Note
delExportConfiguration DELETE ExportViewConfiguration/:view/:id DOC module is deprecated. Use EXP module with JS exports instead. Will be discontinued at end of 2028
delExportTemplate DELETE Templates/:id DOC module is deprecated. Use EXP module with JS exports instead. Will be discontinued at end of 2028
getAvailableExports GET AvailableExports/:view DOC module is deprecated. Use EXP module with JS exports instead. Will be discontinued at end of 2028
getExportConfiguration GET ExportViewConfiguration/:view DOC module is deprecated. Use EXP module with JS exports instead. Will be discontinued at end of 2028
getTemplates GET Templates/:exportType DOC module is deprecated. Use EXP module with JS exports instead. Will be discontinued at end of 2028
handleExportRequest POST Export DOC module is deprecated. Use EXP module with JS exports instead. Will be discontinued at end of 2028
putExportConfiguration PUT ExportViewConfiguration/:id DOC module is deprecated. Use EXP module with JS exports instead. Will be discontinued at end of 2028
putExportTemplate PUT Templates/:id DOC module is deprecated. Use EXP module with JS exports instead. Will be discontinued at end of 2028

DPO — Disposition views

The whole module is retired, except delWorkItem, which redirects into PM. See section 8.2.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
delWorkItem DELETE DispoView/ByResource/:resourceId/WorkItem/:itemId pm.delProjectWorkItem DELETE PM/ProjectWorkItem/:projectWorkItemId

Retired — no V3 successor; the endpoint disappears with API V2.

V2 endpoint V2 path Note
assignToOtherResource POST DispoView/ByResource/:resourceId/AssignToOtherResource This endpoint will not be available in API V3 and will be discontinued at end of 2028
delDispoByResource POST DispoView/ByResource/:resourceId/DeleteItem This endpoint will not be available in API V3 and will be discontinued at end of 2028
getAbsenceBlocksByResource GET DispoView/ByResource/:resourceId/AbsenceBlocks This endpoint will not be available in API V3 and will be discontinued at end of 2028
getDispoByResource GET DispoView/ByResource/:resourceId This endpoint will not be available in API V3 and will be discontinued at end of 2028
getWorkItem GET DispoView/ByResource/:resourceId/WorkItem/:itemId This endpoint will not be available in API V3 and will be discontinued at end of 2028
moveDispoByResource POST DispoView/ByResource/:resourceId/MoveItem This endpoint will not be available in API V3 and will be discontinued at end of 2028
newDispoByResource POST DispoView/ByResource/:resourceId/NewItem This endpoint will not be available in API V3 and will be discontinued at end of 2028
putWorkItem PUT DispoView/ByResource/:resourceId/WorkItem/:itemId This endpoint will not be available in API V3 and will be discontinued at end of 2028

EXP — Exports

Export definitions are addressed as JavaScriptExportDefinition and are written through the translated variant, so a definition always carries its language versions: putExportDefinition → putExportDefinitionTranslated, delExportDefinition → delExportDefinitionTranslated. getExportDefinitionsForObjectType moved the object type to the front of the URL (ObjectType/:objectType/JavaScriptExportDefinitions).

Both export request endpoints redirect unchanged, so running an export is a URL change only.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
delExportDefinition DELETE JavaScriptExportDefinition/:javaScriptExportDefinitionId delExportDefinitionTranslated DELETE JavaScriptExportDefinition/:javaScriptExportDefinitionId/WithTranslations
handleJavaScriptExportRequest POST JavaScriptExport/:javaScriptExportDefinitionId/:objectType/:objectId handleJavaScriptExportRequest POST JavaScriptExport/:javaScriptExportDefinitionId/:objectType/:objectId
handleJavaScriptExportRequestWithSettings POST JavaScriptExport/:javaScriptExportDefinitionId/:objectType/:objectId/WithSettings handleJavaScriptExportRequestWithSettings POST JavaScriptExport/:javaScriptExportDefinitionId/:objectType/:objectId/WithSettings

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getAllExportDefinitions GET JavaScriptExportDefinitions getAllExportDefinitions GET JavaScriptExportDefinitions Returns references to all JavaScript export definitions; read them with getExportDefinitions
getExportDefinition GET JavaScriptExportDefinition/:javaScriptExportDefinitionId getExportDefinition GET JavaScriptExportDefinition/:javaScriptExportDefinitionId Response body changed
getExportDefinitionsForObjectType GET JavaScriptExportDefinitions/:objectType getExportDefinitionsForObjectType GET ObjectType/:objectType/JavaScriptExportDefinitions Expects the V3 type name (ProjectWorkItem instead of Phase) and returns references; read them with getExportDefinitions
putExportDefinition PUT JavaScriptExportDefinition/:javaScriptExportDefinitionId putExportDefinitionTranslated PUT JavaScriptExportDefinition/:javaScriptExportDefinitionId/WithTranslations Request body changed

New in V3 — no V2 equivalent.

getExportDefinitionTranslated, getExportDefinitions, getExportDefinitionsTranslated

FCO — Financial controlling (costs, budgets, currencies)

Financial controlling is fully in V3: the master data — currencies and their exchange rates, the currency calculator, costs types and costs type groups —, the project costs — costs positions and their actuals, the costs overviews and the costs timelines —, the budgets — budget positions, budget assignments, budget lines and the external, foreign and sub-object budgets — and the planned costs of work items and resource allocations.

  • Lists become references plus a bulk read. getCurrencies, getCostsTypes and getCostsTypeGroups (and their translated variants) split into getAllCurrencies / getAllCostsTypes / getAllCostsTypeGroups (references, sortable) and a bulk read by ID. Careful: the V3 getCostsTypes, getCostsTypesTranslated, getCostsTypeGroups, getCostsTypeGroupsTranslated and getCurrencies keep their V2 names but read by ID — they no longer return everything.
  • A costs type carries groupId, not the group. V2 nested the whole costs type group; V3 returns its ID (and getAllCostsTypes returns it as the parent of each reference). Read the group with getCostsTypeGroup.
  • Writes are translated and one at a time. putCurrency becomes putCurrencyTranslated, and the bulk writes putCostsTypesTranslated / putCostsTypeGroupsTranslated have no V3 successor beyond the single put…Translated. delCurrency redirects to delCurrencyTranslated — there is only one delete.
  • Costs centers and pay plans are enum values. They are values of fixed PF enum types, so V3 has no FCO endpoint for them. getCostsCenterEnumType and getPayPlanEnumType return the reference of the enum type; pass its ID to PF getAllEnumValues, getEnumValues or getEnumValue.
  • Exchange rates check the currency. The exchange-rate endpoints answer 404 for an unknown currency — through the redirect, V2 callers of getTodaysCurrencyExchangeRate and the …ExchangeRateAt family see it too.
  • Costs positions and actuals are addressed by their own ID. The project (and, for an actual, the costs position) no longer appears in the path of a read, write or delete. Adding an actual needs only its costs position.
  • A costs position carries IDs and stored amounts. getCostsPosition(s) return costsTypeId, costsCenterId and payPlanId instead of references, and the planned, supplementary and remaining amounts as they were entered — in their own currency, not converted. The converted amounts and the calculated provisional, actual and available amounts come from getCostsPosition(s)Converted, which takes a mandatory targetCurrencyId. The same split applies to actuals: getCostsActual(s) and getCostsActual(s)Converted.
  • Costs overviews and timelines rename their parameters. refid → referencePortfolioId, beg → start, currency → targetCurrencyId, and the timeline filters filterFlow, filterType, filterTypeGroup, filterContainer, filterProject, filterPosition and filterMgmtPhase become flowFilter, costsTypeFilter, costsTypeGroupFilter, costsContainerFilter, projectFilter, costsPositionFilter and managementPhaseFilter. The filters take IDs, and a null element matches costs without that dimension. The grouping values are spelled in PascalCase (Container, Flow, …). The response bodies are restructured.
  • Budget positions and budget assignments are kept apart. V2 called both a "budget position": the one a portfolio owns, and the share of one that was inherited by — assigned to — a sub-portfolio or a project. V3 has separate endpoints for each: …BudgetPosition… for an owned position, …PortfolioBudgetAssignment… and …ProjectBudgetAssignment… for an assigned share. Each accepts only its own ID, given alone without the portfolio or project. Creating an inherited budget (V2 inheritFrom) is addPortfolioBudgetAssignment.
  • A project's budgets are budget lines. getProjectBudgetLines / getProjectsBudgetLines return the calculated amounts per line and name the assignment behind each one; read the assignments themselves with getProjectBudgetAssignments. Inherited positions of a portfolio are read the same way with getPortfolioBudgetLines / getPortfoliosBudgetLines.
  • External, foreign and sub-object budgets rename their parameters like the costs overviews (refid → referencePortfolioId, beg → start, currency → targetCurrencyId), and end is now exclusive. Every entry carries the references budgetPosition and projectPortfolio. SubObj is spelled SubObject. For a project, the foreign and sub-object budgets report the amount the costs timeline carries over the requested period instead of the whole assigned amount, so that they add up to the external budgets.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
addProjectCostsPosition POST ProjectCosts/:projectId/CostsPositions/Add addCostsPosition POST ProjectCosts/:projectId/CostsPositions/Add
addProjectCostsPositionActual POST ProjectCosts/:projectId/CostsPosition/:posId/CostsActuals/Add addCostsActual POST CostsPosition/:costsPositionId/CostsActuals/Add
delCostsTypeGroupTranslated DELETE CostsTypeGroup/:costsTypeGroupId/WithTranslations delCostsTypeGroupTranslated DELETE CostsTypeGroup/:costsTypeGroupId/WithTranslations
delCostsTypeTranslated DELETE CostsType/:costsTypeId/WithTranslations delCostsTypeTranslated DELETE CostsType/:costsTypeId/WithTranslations
delCurrency DELETE Currency/:currencyId delCurrencyTranslated DELETE Currency/:currencyId/WithTranslations
delCurrencyExchangeRateAt DELETE Currency/:currencyId/ExchangeRate/At/:date delCurrencyExchangeRateAt DELETE Currency/:currencyId/ExchangeRate/At/:day
delProjectCostsPosition DELETE ProjectCosts/:projectId/CostsPosition/:positionId delCostsPosition DELETE CostsPosition/:costsPositionId
delProjectCostsPositionActual DELETE ProjectCosts/:projectId/CostsPosition/:posId/CostsActual/:actualId delCostsActual DELETE CostsActual/:costsActualId
getCurrencyExchangeRateAt GET Currency/:currencyId/ExchangeRate/At/:date getCurrencyExchangeRateAt GET Currency/:currencyId/ExchangeRate/At/:day
getPortfolioBudgetAssignmentParents GET PortfolioBudgetAssignment/:assId/Parent getPortfolioBudgetAssignmentParents GET PortfolioBudgetAssignment/:budgetAssignmentId/Parents
getPortfolioBudgetPositionParent GET PortfolioBudgetPosition/:bupId/Parent getBudgetPositionParent GET BudgetPosition/:budgetPositionId/Parent
getProjectBudgetAssignmentParents GET ProjectBudgetAssignment/:assId/Parent getProjectBudgetAssignmentParents GET ProjectBudgetAssignment/:budgetAssignmentId/Parents
getTodaysCurrencyExchangeRate GET Currency/:currencyId/ExchangeRate/Today getCurrencyExchangeRateToday GET Currency/:currencyId/ExchangeRate/Today
putCurrencyExchangeRateAt PUT Currency/:currencyId/ExchangeRate/At/:date putCurrencyExchangeRateAt PUT Currency/:currencyId/ExchangeRate/At/:day

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
addPortfolioBudgetPosition POST PortfolioCosts/:ppfId/BudgetPositions/Add addBudgetPosition POST ProjectPortfolio/:projectPortfolioId/BudgetPositions/Add Names the sort index sortIndex and the currency currencyId, and also takes name, code, description, color, iconRef, periodStart and periodEnd; inheriting via inheritFrom is addPortfolioBudgetAssignment (POST ProjectPortfolio/:projectPortfolioId/BudgetAssignments/Add), which takes budgetPositionId and currencyId and no sort index
addProjectBudgetPosition POST ProjectCosts/:projectId/BudgetPositions/Add addProjectBudgetAssignment POST Project/:projectId/BudgetAssignments/Add Names the granting portfolio sourcePortfolioId, the budget position budgetPositionId and the currency currencyId; takes the root budget position only, not the ID of a budget assignment
calculateSum POST Calculator/Sum calculateSum POST Calculator/Sum Names the currency currencyId
convertCurrency GET CurrencyExchange/:currencyId/:amount convertCurrency GET CurrencyExchange/:currencyId/:amount Names the currency currencyId
delPortfolioBudgetPosition DELETE PortfolioCosts/:ppfId/BudgetPosition/:bupId delBudgetPosition DELETE BudgetPosition/:budgetPositionId Addressed by the position's ID alone; a share assigned to the portfolio is deleted with delPortfolioBudgetAssignment (DELETE PortfolioBudgetAssignment/:budgetAssignmentId)
delProjectBudgetPosition DELETE ProjectCosts/:projectId/BudgetPosition/:bupId delProjectBudgetAssignment DELETE ProjectBudgetAssignment/:budgetAssignmentId Addressed by the assignment's ID alone, without the project
getCostsCenter GET CostsCenter/:costsCenterId getCostsCenterEnumType GET CostsCenterEnumType Costs centers are values of a fixed PF enum type; this returns the type's reference, read the value with PF getEnumValue
getCostsCenters GET CostsCenters getCostsCenterEnumType GET CostsCenterEnumType Costs centers are values of a fixed PF enum type; this returns the type's reference, PF getAllEnumValues returns references to the values, read them with PF getEnumValues
getCostsType GET CostsType/:costsTypeId getCostsType GET CostsType/:costsTypeId Returns the group's ID in groupId instead of the nested group; read the group with getCostsTypeGroup
getCostsTypeGroup GET CostsTypeGroup/:costsTypeGroupId getCostsTypeGroup GET CostsTypeGroup/:costsTypeGroupId Response body changed
getCostsTypeGroups GET CostsTypeGroups getAllCostsTypeGroups GET CostsTypeGroups Returns references; read them with getCostsTypeGroups, which reads by ID
getCostsTypeGroupsTranslated GET CostsTypeGroups/WithTranslations getAllCostsTypeGroups GET CostsTypeGroups Returns references; read them with getCostsTypeGroupsTranslated, which reads by ID
getCostsTypeGroupTranslated GET CostsTypeGroup/:costsTypeGroupId/WithTranslations getCostsTypeGroupTranslated GET CostsTypeGroup/:costsTypeGroupId/WithTranslations Response body changed
getCostsTypes GET CostsTypes getAllCostsTypes GET CostsTypes Returns references with the parent group; read them with getCostsTypes, which reads by ID
getCostsTypesTranslated GET CostsTypes/WithTranslations getAllCostsTypes GET CostsTypes Returns references with the parent group; read them with getCostsTypesTranslated, which reads by ID
getCostsTypeTranslated GET CostsType/:costsTypeId/WithTranslations getCostsTypeTranslated GET CostsType/:costsTypeId/WithTranslations Returns the group's ID in groupId instead of the nested group
getCurrencies GET Currencies getAllCurrencies GET Currencies Returns references; read them with getCurrencies, which reads by ID
getCurrency GET Currency/:currencyId getCurrency GET Currency/:currencyId Response body changed
getCurrencyExchangeRates GET Currency/:currencyId/ExchangeRates getCurrencyExchangeRates GET Currency/:currencyId/ExchangeRates Answers 404 for an unknown currency instead of an empty rate set
getHomeCurrency GET HomeCurrency getHomeCurrency GET HomeCurrency Response body changed
getMacroAllocationPlannedCosts GET MacroAllocationCosts/:macroAllocationId/PlannedCosts getResourceAllocationPlannedCosts GET ResourceAllocationCosts/:resourceAllocationId/PlannedCosts The macro allocation is called a resource allocation
getPayPlan GET PayPlan/:payPlanId getPayPlanEnumType GET PayPlanEnumType Pay plans are values of a fixed PF enum type; this returns the type's reference, read the value with PF getEnumValue
getPayPlans GET PayPlans getPayPlanEnumType GET PayPlanEnumType Pay plans are values of a fixed PF enum type; this returns the type's reference, PF getAllEnumValues returns references to the values, read them with PF getEnumValues
getPortfolioBudgetPosition GET PortfolioCosts/:ppfId/BudgetPosition/:bupId getBudgetPosition GET BudgetPosition/:budgetPositionId Addressed by the position's ID alone; a share assigned to the portfolio is read with getPortfolioBudgetAssignment (GET PortfolioBudgetAssignment/:budgetAssignmentId)
getPortfolioBudgetPositions GET PortfolioCosts/:ppfId/BudgetPositions getAllBudgetPositionsOfProjectPortfolio GET ProjectPortfolio/:projectPortfolioId/BudgetPositions Returns references to the positions the portfolio owns, with the portfolio as parent; read them with getBudgetPositions, which reads by ID. Inherited positions are not listed — read them with getPortfolioBudgetLines
getPortfolioBudgetPositionTimeline GET PortfolioCosts/:ppfId/BudgetPosition/Timeline/:bupId getBudgetPositionTimeline GET BudgetPosition/:budgetPositionId/Timeline Addressed by the position's ID alone; names the range from / until, adds limit, drops currency and returns a pf.VersionTimeline with the latest version per slot; the timeline of an assignment is getPortfolioBudgetAssignmentTimeline
getPortfolioCostsOverview GET PortfolioCosts/:ppfId/Overview getPortfolioCostsOverview GET PortfolioCosts/:projectPortfolioId/Overview Names the reference portfolio referencePortfolioId, the range start / end and the currency targetCurrencyId; the costs overview has a different structure
getPortfolioCostsTimeline GET PortfolioCosts/:ppfId/CostsTimeline getPortfolioCostsTimeline GET PortfolioCosts/:projectPortfolioId/CostsTimeline Renamed parameters and filters (see above); the costs matrix has a different structure
getPortfolioExternalBudgets GET PortfolioCosts/:ppfId/ExternalBudgets getPortfolioExternalBudgets GET PortfolioCosts/:projectPortfolioId/ExternalBudgets Names the reference portfolio referencePortfolioId, the range start / end (end now exclusive) and the currency targetCurrencyId; every entry carries the references budgetPosition and projectPortfolio
getPortfolioForeignBudgets GET PortfolioCosts/:ppfId/ForeignBudgets getPortfolioForeignBudgets GET PortfolioCosts/:projectPortfolioId/ForeignBudgets Parameters and entries as getPortfolioExternalBudgets
getPortfoliosBudgetPositions GET PortfoliosCosts/BudgetPositions getAllBudgetPositions GET BudgetPositions No portfolioid filter: returns references to the positions owned by all portfolios, each with its portfolio as parent; read them with getBudgetPositions, which reads by ID. Inherited positions are not listed — read them with getPortfoliosBudgetLines
getPortfoliosCostsOverviews GET PortfoliosCosts/Overviews getPortfoliosCostsOverviews GET PortfoliosCosts/:projectPortfolioIds/Overviews Takes the portfolio IDs in the path instead of portfolioid; parameters and response as getPortfolioCostsOverview
getPortfolioSubObjBudgets GET PortfolioCosts/:ppfId/SubObjBudgets getPortfolioSubObjectBudgets GET PortfolioCosts/:projectPortfolioId/SubObjectBudgets Spelled SubObject; parameters and entries as getPortfolioExternalBudgets
getProjectBudgetPosition GET ProjectCosts/:projectId/BudgetPosition/:bupId getProjectBudgetAssignment GET ProjectBudgetAssignment/:budgetAssignmentId Addressed by the assignment's ID alone, without the project
getProjectBudgetPositions GET ProjectCosts/:projectId/BudgetPositions getProjectBudgetLines GET Project/:projectId/BudgetLines Returns the project's budget lines with the calculated amounts, each naming the assignment behind it; read the assignments with getProjectBudgetAssignments
getProjectCostsActuals GET ProjectCosts/:projectId/CostsActuals getCostsActualsOfProject GET ProjectCosts/:projectId/CostsActuals Returns references, no longer grouped by costs position — sort by costsPositionId or call getCostsActualsOfCostsPosition per position to rebuild the groups; read the actuals with getCostsActuals
getProjectCostsOverview GET ProjectCosts/:prjId/Overview getProjectCostsOverview GET ProjectCosts/:projectId/Overview Names the reference portfolio referencePortfolioId, the range start / end and the currency targetCurrencyId; the costs overview has a different structure
getProjectCostsPosition GET ProjectCosts/:projectId/CostsPosition/:positionId getCostsPosition GET CostsPosition/:costsPositionId Addressed by the costs position's ID alone; returns costsTypeId / costsCenterId / payPlanId and the amounts as entered — read the converted and calculated amounts with getCostsPositionConverted
getProjectCostsPositionActual GET ProjectCosts/:projectId/CostsPosition/:posId/CostsActual/:actualId getCostsActual GET CostsActual/:costsActualId Addressed by the costs actual's ID alone; returns the amount as entered — read the converted amount with getCostsActualConverted
getProjectCostsPositionActualParents GET ProjectCostsPositionActual/:actualId/Parents getCostsActual GET CostsActual/:costsActualId Obsolete: an actual reports its one parent in costsPositionId
getProjectCostsPositionActuals GET ProjectCosts/:projectId/CostsPosition/:posId/CostsActuals getCostsActualsOfCostsPosition GET CostsPosition/:costsPositionId/CostsActuals Returns references with the costs position as parent; read the actuals with getCostsActuals, which reads by ID
getProjectCostsPositionCostsTimeline GET ProjectCosts/:projectId/CostsPosition/:positionId/CostsTimeline getCostsPositionCostsTimeline GET CostsPosition/:costsPositionId/CostsTimeline Addressed by the costs position's ID alone; renamed parameters and filters (see above); the costs matrix has a different structure
getProjectCostsPositionParents GET ProjectCostsPosition/:positionId/Parents getAllCostsPositions GET CostsPositions Obsolete: a costs position has exactly one parent, its project, reported in projectId and as the parent of each reference
getProjectCostsPositions GET ProjectCosts/:projectId/CostsPositions getCostsPositionsOfProject GET ProjectCosts/:projectId/CostsPositions Returns references with the project as parent; read the positions with getCostsPositions, which reads by ID
getProjectCostsTimeline GET ProjectCosts/:projectId/CostsTimeline getProjectCostsTimeline GET ProjectCosts/:projectId/CostsTimeline Renamed parameters and filters (see above); the costs matrix has a different structure
getProjectExternalBudgets GET ProjectCosts/:prjid/ExternalBudgets getProjectExternalBudgets GET ProjectCosts/:projectId/ExternalBudgets Names the reference portfolio referencePortfolioId, the range start / end (end now exclusive) and the currency targetCurrencyId; every entry carries the references budgetPosition and projectPortfolio
getProjectForeignBudgets GET ProjectCosts/:prjid/ForeignBudgets getProjectForeignBudgets GET ProjectCosts/:projectId/ForeignBudgets Parameters and entries as getProjectExternalBudgets; reports the amount the costs timeline carries over the requested period instead of the whole assigned amount
getProjectPhasePlannedCosts GET ProjectPhaseCosts/:phaseId/PlannedCosts getProjectWorkItemPlannedCosts GET ProjectWorkItemCosts/:projectWorkItemId/PlannedCosts The phase is called a project work item (section 6)
getProjectsBudgetPositions GET ProjectsCosts/BudgetPositions getProjectsBudgetLines GET Projects/:projectIds/BudgetLines Takes the project IDs in the path instead of projectid; returns budget lines as getProjectBudgetLines
getProjectScenarioCostsOverview GET ScenarioCosts/:projectScenarioId/Overview getProjectScenarioCostsOverview GET ScenarioCosts/:projectScenarioId/Overview Names the reference portfolio referencePortfolioId, the range start / end and the currency targetCurrencyId; the costs overview has a different structure
getProjectScenarioCostsTimeline GET ScenarioCosts/:projectScenarioId/CostsTimeline getProjectScenarioCostsTimeline GET ScenarioCosts/:projectScenarioId/CostsTimeline Renamed parameters and filters (see above); the costs matrix has a different structure
getProjectsCostsOverviews GET ProjectsCosts/Overviews getProjectsCostsOverviews GET ProjectsCosts/:projectIds/Overviews Takes the project IDs in the path instead of projectid; parameters and response as getProjectCostsOverview
getProjectSubObjBudgets GET ProjectCosts/:prjid/SubObjBudgets getProjectSubObjectBudgets GET ProjectCosts/:projectId/SubObjectBudgets Spelled SubObject; parameters, entries and amounts as getProjectForeignBudgets
moveCostsPositionActual POST ProjectCosts/:projectId/CostsPosition/:posId/CostsActual/:actualId/Move moveCostsPositionActual POST CostsPosition/:costsPositionId/CostsActual/:costsActualId/Move/:targetCostsPositionId No project in the path; the target costs position is a mandatory path segment instead of the optional targetId; checks the update permission on both positions
putCostsTypeGroupsTranslated PUT CostsTypeGroups/WithTranslations putCostsTypeGroupTranslated PUT CostsTypeGroup/:costsTypeGroupId/WithTranslations No bulk write; write the groups one at a time
putCostsTypeGroupTranslated PUT CostsTypeGroup/:costsTypeGroupId/WithTranslations putCostsTypeGroupTranslated PUT CostsTypeGroup/:costsTypeGroupId/WithTranslations Takes the V3 CostsTypeGroupTranslated as its body
putCostsTypesTranslated PUT CostsTypes/WithTranslations putCostsTypeTranslated PUT CostsType/:costsTypeId/WithTranslations No bulk write; write the costs types one at a time
putCostsTypeTranslated PUT CostsType/:costsTypeId/WithTranslations putCostsTypeTranslated PUT CostsType/:costsTypeId/WithTranslations Takes the V3 CostsTypeTranslated as its body, with groupId instead of the nested group
putCurrency PUT Currency/:currencyId putCurrencyTranslated PUT Currency/:currencyId/WithTranslations Takes the V3 CurrencyTranslated as its body, with all translations
putPortfolioBudgetPosition PUT PortfolioCosts/:ppfId/BudgetPosition/:bupId putBudgetPosition PUT BudgetPosition/:budgetPositionId Addressed by the position's ID alone; a share assigned to the portfolio is written with putPortfolioBudgetAssignment (PUT PortfolioBudgetAssignment/:budgetAssignmentId)
putProjectBudgetPosition PUT ProjectCosts/:projectId/BudgetPosition/:bupId putProjectBudgetAssignment PUT ProjectBudgetAssignment/:budgetAssignmentId Addressed by the assignment's ID alone, without the project; writes only the amount
putProjectCostsPosition PUT ProjectCosts/:projectId/CostsPosition/:positionId putCostsPosition PUT CostsPosition/:costsPositionId Addressed by the costs position's ID alone; the body takes costsTypeId / costsCenterId / payPlanId instead of references, and its projectId must not differ from the stored one
putProjectCostsPositionActual PUT ProjectCosts/:projectId/CostsPosition/:posId/CostsActual/:actualId putCostsActual PUT CostsActual/:costsActualId Addressed by the costs actual's ID alone; moving it to another costs position is moveCostsPositionActual

New in V3 — no V2 equivalent.

getAllBudgetAssignmentsOfProject, getAllBudgetAssignmentsOfProjectPortfolio, getAllPortfoliosBudgetAssignments, getAllProjectsBudgetAssignments, getCostsActualIdentification, getCostsActualIdentifications, getCostsActualsConverted, getCostsPositionIdentification, getCostsPositionIdentifications, getCostsPositionsConverted, getCurrenciesTranslated, getCurrencyTranslated, getPortfolioBudgetAssignments

LCY — Lifecycles

Fully migrated, but 27 of 34 endpoints are reimplemented rather than redirected: the lifecycle contracts were rebuilt around references and explicit sub-states. Budget time for this module even though nothing is retired.

New capability worth adopting: getAllStates and getAllTransitions for the reference lists, getObjectsByStates to query several states at once, and getStateSubStates / getStateAllSubStates for the sub-state hierarchy.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
delStateTranslated DELETE State/:stateId/WithTranslations delStateTranslated DELETE State/:stateId/WithTranslations
delSubStateTranslated DELETE State/:stateId/SubState/:subStateId/WithTranslations delSubStateTranslated DELETE State/:stateId/SubState/:subStateId/WithTranslations
delTransitionTranslated DELETE Transition/:transitionId/WithTranslations delTransitionTranslated DELETE Transition/:transitionId/WithTranslations
delTypeInitialState DELETE Type/:objtype/InitialState delTypeInitialState DELETE Type/:objectType/InitialState
getObjectsByState GET State/:stateId/:objType/Objects getObjectsByState GET State/:stateId/Objects/:objectType
getStuckObjects GET Objects/:objtype/Stuck getStuckObjects GET Objects/:objectType/Stuck
putTypeInitialState PUT Type/:objtype/InitialState putTypeInitialState PUT Type/:objectType/InitialState

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getInitialStates GET InitialStates getInitialStates GET InitialStates Returns references to the states and sub-states together with the object type, and getStates to read the states by ID
getObjectHistory GET Object/:objtype/:objid/History getObjectHistory GET Object/:objectType/:objectId/History Returns the entries newest first with references to the state, the transition and the initiating user
getObjectReachableStates GET Object/:objtype/:objid/ReachableStates getObjectReachableStates GET Object/:objectType/:objectId/ReachableStates Returns references to the states; read them with getStates
getObjectsHistories GET Objects/:objtype/:objids/Histories getObjectsHistories GET Objects/:objectType/:objectIds/Histories Returns the entries newest first with references to the state, the transition and the initiating user
getObjectsReachableStates GET Objects/:objtype/:objids/ReachableStates getObjectsReachableStates GET Objects/:objectType/:objectIds/ReachableStates Returns references to the states; read them with getStates
getObjectsStates GET Objects/:objtype/:objids/States getObjectsStates GET Objects/:objectType/:objectIds/States Returns references to the states and sub-states, and getStates to read the states by ID
getObjectState GET Object/:objtype/:objid/State getObjectState GET Object/:objectType/:objectId/State Returns references to the state and sub-state, and getState to read the state
getObjectsTransitions GET Objects/:objtype/:objids/Transitions getObjectsTransitions GET Objects/:objectType/:objectIds/Transitions Returns references to the transitions; read them with getTransitions
getObjectTransitions GET Object/:objtype/:objid/Transitions getObjectTransitions GET Object/:objectType/:objectId/Transitions Returns references to the transitions; read them with getTransitions
getState GET State/:stateId getState GET State/:stateId Response body changed
getStateReachableStates GET State/:stateId/ReachableStates getStateReachableStates GET State/:stateId/ReachableStates Returns references to the states; read them with getStates
getStates GET States getAllStates GET States Returns references; read them with getStates
getStatesTranslated GET States/WithTranslations getAllStates GET States Returns references; read them with getStatesTranslated
getStateTransitions GET State/:stateId/Transitions getStateTransitions GET State/:stateId/Transitions Returns references to the transitions; read them with getTransitions
getStateTranslated GET State/:stateId/WithTranslations getStateTranslated GET State/:stateId/WithTranslations Response body changed
getSubStatesTranslated GET State/:stateId/SubStates/WithTranslations getStateAllSubStates GET State/:stateId/SubStates Returns references; read them with getSubStatesTranslated
getSubStateTranslated GET State/:stateId/SubState/:subStateId/WithTranslations getSubStateTranslated GET State/:stateId/SubState/:subStateId/WithTranslations Response body changed
getTransition GET Transition/:transitionId getTransition GET Transition/:transitionId from and to are references to the state and sub-state; read the states with getStates
getTransitions GET Transitions getAllTransitions GET Transitions Returns references; read them with getTransitions
getTransitionTranslated GET Transition/:transitionId/WithTranslations getTransitionTranslated GET Transition/:transitionId/WithTranslations Response body changed
getTypeInitialState GET Type/:objtype/InitialState getTypeInitialState GET Type/:objectType/InitialState Returns references to the state and sub-state, and getState to read the state
getTypeReachableStates GET Type/:objtype/ReachableStates getTypeReachableStates GET Type/:objectType/ReachableStates Returns references to the states; read them with getStates
migrateObjects POST Objects/:objtype/Migrate/:sourceStateId/:sourceSubStateId/To/:targetStateId/:targetSubStateId migrateObjects POST Objects/:objectType/Migrate/:sourceStateId/:sourceSubStateId/To/:targetStateId/:targetSubStateId Takes the remark in its body as the V3 LifecycleUpgradeRemark
putStateTranslated PUT State/:stateId/WithTranslations putStateTranslated PUT State/:stateId/WithTranslations Takes the V3 LifecycleStateTranslated as its body
putSubStateTranslated PUT State/:stateId/SubState/:subStateId/WithTranslations putSubStateTranslated PUT State/:stateId/SubState/:subStateId/WithTranslations Takes the V3 LifecycleSubStateTranslated as its body
putTransitionTranslated PUT Transition/:transitionId/WithTranslations putTransitionTranslated PUT Transition/:transitionId/WithTranslations Takes the V3 LifecycleTransitionTranslated as its body
upgradeObject POST Object/:objtype/:objid/Upgrade/:transition upgradeObject POST Object/:objectType/:objectId/Upgrade/:transitionId/WithRemark Takes the remark in its body as the V3 LifecycleUpgradeRemark and returns references to the reached state and sub-state

New in V3 — no V2 equivalent.

getObjectsByStates, getStateSubStates, getTransitionsTranslated

LIC — Licensing

getLicenseStatus is the only LIC endpoint that stays available to public-API and JavaScript callers, and it is the one an integration normally wants: it reports what the tenant is licensed for.

The licence administration endpoints — getLicenses, putLicenses, getUsages, initTenants — moved to the maintenance interface in V3 and cannot be called with a bearer token. If your tooling uses them, it needs the maintenance access kind, which is a different authentication mechanism entirely.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getLicenses GET Licenses getLicenses GET Licenses V3 successor is reachable only from the browser client or the maintenance interface
getLicenseStatus GET License/Status getLicenseStatus GET License/Status Names the members validUntil and admin expiry and adminEmail
getUsages GET Licenses/Usage getUsages GET Licenses/Usage V3 successor is reachable only from the browser client or the maintenance interface
initTenants POST Tenants/Init initTenants POST Tenants/Init V3 successor is reachable only from the browser client or the maintenance interface
putLicenses PUT Licenses putLicenses PUT Licenses V3 successor is reachable only from the browser client or the maintenance interface

MAIN — Server information and session start

  • getStartUrl → getHomeUrl (JavaScript only).
  • The OpenAPI description endpoints lose the standard-version segment from their URL and now emit OpenAPI 3.2.0; getTypeScriptDefinition is new. See section 3.
  • getVersion (GET /API/V3/MAIN/Versions) is unchanged in spirit and is still the recommended compatibility check at start-up.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
getObjectUrl JS only getObjectUrl JS only
getServerStatus GET ServerStatus dgn.getServerStatus GET DGN/ServerStatus
getStartUrl JS only getHomeUrl JS only

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getOpenApi3Json GET ApiDescription/OpenApi/303/Json getOpenApi3Json GET ApiDescription/OpenApi/Json Describes API V3 in OpenAPI 3.2.0; an OpenAPI 3.0.3 rendition is not available
getOpenApi3Yaml GET ApiDescription/OpenApi/303/Yaml getOpenApi3Yaml GET ApiDescription/OpenApi/Yaml Describes API V3 in OpenAPI 3.2.0; an OpenAPI 3.0.3 rendition is not available
getVersion GET Versions getVersion GET Versions Reports the version and fingerprint of API V3

New in V3 — no V2 equivalent.

getTypeScriptDefinition

MSG — Messages, forwarding rules and mail

The inbox and the forwarding rules of the calling user now live under CurrentUser/; an administrator manages another resource's rules under Resource/:resourceId/.

  • Inbox: getAllCurrentUserMessages returns references (most recent first, sortable, includeRead now really filters), getCurrentUserMessage / getCurrentUserMessages return the messages. In a message, source, trigger, createdBy and every shown addressee are references; the trigger's text moved to triggerReason. Reading and deleting act on the caller's own copy only — the other recipients keep theirs. Both were not implemented in V2 and work now, on the V2 URL as well.
  • Forwarding rules: getAll…MessageForwardRules returns references in the order the rules are applied; reading and deleting are bulk operations, and a rule of another resource is simply not found. subjectRegEx was never stored in V2 and is now. A regular expression that does not compile answers 422, and a rule ID must be a well-formed UUID.
  • Posting: postCurrentUserMessage answers 201. The urgency / privacy values are spelled BreakingNews, Public, … instead of BREAKING_NEWS, PUBLIC, and an addressee without a user answers 422.
  • forwardedAt is now filled when a message was forwarded by email.

If you maintain forwarding rules that filter on an object type pattern, read section 6: a pattern written to match the old work-item type name no longer matches work items.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
delMessage DELETE Message/:messageId delCurrentUserMessage DELETE CurrentUser/Message/:messageId
getMessageTriggers GET MessageTriggers getAllMessageTriggers GET MessageTriggers
putMessageForwardRule PUT ForwardRule/:forwardRuleId putCurrentUserMessageForwardRule PUT CurrentUser/MessageForwardRule/:ruleId
putMessageForwardRuleOfResource PUT Resource/:resourceId/ForwardRule/:forwardRuleId putResourceMessageForwardRule PUT Resource/:resourceId/MessageForwardRule/:ruleId
readMessage POST Message/:messageId/Read readCurrentUserMessage POST CurrentUser/Message/:messageId/Read
sendMail JS only sendMail JS only
sendMailByTemplate JS only sendMailByTemplate JS only

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
delMessageForwardRule DELETE ForwardRule/:forwardRuleId delCurrentUserMessageForwardRules DELETE CurrentUser/MessageForwardRules/:ruleIds Bulk operation
delMessageForwardRuleOfResource DELETE Resource/:resourceId/ForwardRule/:forwardRuleId delResourceMessageForwardRules DELETE Resource/:resourceId/MessageForwardRules/:ruleIds Bulk operation
getMessage GET Message/:messageId getCurrentUserMessage GET CurrentUser/Message/:messageId Response body changed
getMessageForwardRule GET ForwardRule/:forwardRuleId getCurrentUserMessageForwardRules GET CurrentUser/MessageForwardRules/:ruleIds Bulk operation
getMessageForwardRuleOfResource GET Resource/:resourceId/ForwardRule/:forwardRuleId getResourceMessageForwardRules GET Resource/:resourceId/MessageForwardRules/:ruleIds Bulk operation
getMessageForwardRules GET ForwardRules getAllCurrentUserMessageForwardRules + getCurrentUserMessageForwardRules GET CurrentUser/MessageForwardRules References, then bulk read
getMessageForwardRulesOfResource GET Resource/:resourceId/ForwardRules getAllResourceMessageForwardRules + getResourceMessageForwardRules GET Resource/:resourceId/MessageForwardRules References, then bulk read
getMessages GET Messages getAllCurrentUserMessages + getCurrentUserMessages GET CurrentUser/Messages References, then bulk read
postMessage POST Message/Post postCurrentUserMessage POST CurrentUser/Message/Post Request body changed

MTG — Meetings

Meetings and meeting groups are full V3 object families now: identifications, version timelines, historic versions, and getAllMeetings / getAllMeetingGroups reference lists, plus getRootMeetingGroups.

  • copyMeeting is retired: call addMeeting with the copy parameter (and copyRelations to carry relations over). This is the general V3 pattern — copying is an option of creating, not an endpoint of its own.
  • getMeetingsByRef is replaced by the general relation mechanism, pf.getAllRelations. Read the object's relations, then read the meetings you found among them.
  • addMeetingGroup moved from POST MeetingGroups to POST MeetingGroups/Add, in line with the house form.
  • getSubGroupsOfMeetingGroup becomes getSubGroupsOfMeetingGroup.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
addMeeting POST MeetingGroup/:groupId/Meetings/Add addMeeting POST MeetingGroup/:groupId/Meetings/Add
addMeetingGroup POST MeetingGroups addMeetingGroup POST MeetingGroups/Add
addMeetingGroupSubGroup POST MeetingGroup/:groupId/SubGroups/Add addMeetingGroupSubGroup POST MeetingGroup/:groupId/SubGroups/Add
delMeeting DELETE Meeting/:meetingId delMeeting DELETE Meeting/:meetingId
delMeetingGroup DELETE MeetingGroup/:groupId delMeetingGroup DELETE MeetingGroup/:meetingGroupId
moveGroup POST MeetingGroup/:groupId/MoveTo/:parentGroupId moveGroup POST MeetingGroup/:groupId/Move
moveGroupToTop POST MeetingGroup/:groupId/MoveToTop moveGroupToTop POST MeetingGroup/:groupId/MoveToTop
moveMeeting POST Meeting/:meetingId/Move moveMeeting POST Meeting/:meetingId/Move

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getMeeting GET Meeting/:meetingId getMeeting GET Meeting/:meetingId Response body changed
getMeetingGroup GET MeetingGroup/:groupId getMeetingGroup GET MeetingGroup/:meetingGroupId Response body changed
getMeetingGroups GET MeetingGroups getAllMeetingGroups GET MeetingGroups Returns references; read them with getMeetingGroups
getMeetings GET Meetings getAllMeetings GET Meetings Returns references; read them with getMeetings; the state filter is gone
getMeetingsByRef GET Meetings/ByRef/:reltype/:reftype/:refid pf.getAllRelations GET PF/Relations/OfObject/:objectType/:objectId/All Parameter list changed
getMeetingsOfGroup GET MeetingGroup/:groupId/Meetings getMeetingsOfGroup GET MeetingGroup/:groupId/Meetings Returns references to the meetings of the group; read them with getMeetings; the state filter is not available
getSubGroups GET MeetingGroup/:groupId/SubGroups getSubGroupsOfMeetingGroup GET MeetingGroup/:groupId/SubGroups Returns references to the sub groups; read them with getMeetingGroups
putMeeting PUT Meeting/:meetingId putMeeting PUT Meeting/:meetingId Takes the V3 Meeting as its body
putMeetingGroup PUT MeetingGroup/:groupId putMeetingGroup PUT MeetingGroup/:meetingGroupId Takes the V3 MeetingGroup as its body and only updates an existing group; create one with addMeetingGroup or addMeetingGroupSubGroup

Retired — no V3 successor; the endpoint disappears with API V2.

V2 endpoint V2 path Note
copyMeeting POST Meeting/:meetingId/Copy Use addMeeting with copy parameter, will be discontinued at end of 2028

New in V3 — no V2 equivalent.

getMeetingGroupIdentification, getMeetingGroupIdentifications, getMeetingGroupTimeline, getMeetingGroupVersion, getMeetingIdentification, getMeetingIdentifications, getMeetingTimeline, getMeetingVersion, getRootMeetingGroups

The module is retired except for the full text search. See section 8.3.

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getContextSearch GET Context/:type/:id/Search search POST Search Searches all object types, full text only (no start object, regex, recursive, ranking or followrelations); response is PF.ReferenceWithParent[]
getSearch GET Type/:type search POST Search Searches all object types, full text only (no type, regex or ranking); response is PF.ReferenceWithParent[]

Retired — no V3 successor; the endpoint disappears with API V2.

V2 endpoint V2 path Note
findObjects POST Find This endpoint will not be available in API V3 and will be discontinued at end of 2028
getContext GET Context/:type/:id Navigation will not be available in new client and will be discontinued at end of 2028
getMemberRangeSearch GET Type/:type/MemberRangeSearch This endpoint will not be available in API V3 and will be discontinued at end of 2028
getMemberRegexSearch GET Type/:type/MemberRegexSearch This endpoint will not be available in API V3 and will be discontinued at end of 2028
getMemberTextSearch GET Type/:type/MemberTextSearch This endpoint will not be available in API V3 and will be discontinued at end of 2028
getObject GET Object/:type/:id Navigation will not be available in new client and will be discontinued at end of 2028
getRootContext GET RootContext Navigation will not be available in new client and will be discontinued at end of 2028
getTypeInfos GET TypeInfos This endpoint will not be available in API V3 and will be discontinued at end of 2028

PF — Platform (relations, attachments, properties, enums, icons)

The platform module is where the general mechanisms live, so several other modules' endpoints resolve here.

  • Relations are the general mechanism. getAllRelations replaces module-specific "by reference" endpoints (mtg.getMeetingsByRef) and relation listings (acm.getUserRelations). delExplicitRelation is retired: delete a relation by naming its two ends, not by a relation ID.
  • Documents became proper families: document folders and document links have reference lists, parent lookups, sub-folder creation, and folder-scoped attachment adders.
  • Moving a document folder or link takes two endpoints. V2 had one Move call whose folder parameter meant "the top" when omitted; V3 splits that into MoveTo (destination mandatory) and MoveToTop, so that each call can name the permission it needs. That is also why a put cannot move anything: putDocumentFolder and putDocumentLink answer 400 if the request body changes the parent folder. The move endpoints ask more than V2 did — UPDATE on the folder or link being moved and CREATE below the destination, where V2 only asked UPDATE on the owning object and never checked the destination at all. Moving a folder below itself or below one of its own sub-folders answers 400.
  • getExplicitRelation needs both ends. Reading one requires IDENTIFY on the relation's source object and on its target object — the same rule V2 applied, now documented. It is not redirected because V3's Relation is both slimmer and differently keyed: type now carries the relation type, where V2 put a fixed Relation there and named the relation type in relationType.
  • A relation's type is its relation type, everywhere. The reading endpoints — getExplicitRelation, getForwardRelations, getBackwardRelations, getAllRelations and the getCurrentUser…Relations trio — report type = the relation type, and so does the pf.Reference answered by addExplicitRelation. So the reference you get from Add is the (relationType, relationId) pair that addresses the relation, and you can feed it straight into the Get URL. This differs from V2, where the same Add call answered the fixed Relation type — if you read type off that response, it changes value when you move. The fixed Relation type lives on only as a second identity on the change notification websocket: a relation is announced under both, so a listener is reached whichever one it subscribed to.
  • Enum values: the bulk putEnumValues, putEnumValuesTranslated and delEnumValuesTranslated are retired in favour of the single-value endpoints. getAllEnumTypes and getAllEnumValues are new.
  • Dynamic properties split in two — PropertyDefinition… is the property, PropertyPresentation… is one client's presentation of it and carries the client in the URL. Every V2 property endpoint therefore maps to a presentation endpoint with clientId Client4, except the type list, which is now getAllPropertyObjectTypes and redirects. Section 5.15 has the whole picture, including the order a live object's properties array comes in — read it before rewriting these calls.
  • Icons: getIcon is new, and it is what an iconRef of the form /API/V3/PF/Icon/<id> points at.
  • Attachments keep their binary / JSON / text variants (JavaScript only, as in V2). The …AndLink variants gained the basic field initializers of section 5.8.
  • Retired without a successor: getQrCode, getDataMatrix, getUserAgent, inspectConnection. Generate barcodes and parse user agents on your side.
  • Limits: getMaxParallelRequests moved from Limitations/MaxParallelRequests to Limits/MaxParallelRequests and renamed its response member from limit to maxParallel. It is not redirected, so this one needs a rewrite. getServerConfig, getConfig and getUndeleteLimit are unchanged and redirect — see section 5.13 for what each of them is for.
  • WebSocket: getNewWebSocketConnection redirects to the change-notification WebSocket. That makes it bearer-token only and limits it to change notifications (section 5.14).

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
addAttachment POST Attachment/Add addAttachment POST Attachment/Add
addAttachmentBinary JS only addAttachmentBinary JS only
addAttachmentJson JS only addAttachmentJson JS only
addAttachmentText JS only addAttachmentText JS only
addExplicitRelation POST ExplicitRelations/:relationType/Add addExplicitRelation POST ExplicitRelations/:relationType/Add
delConfig DELETE Config/:configId delConfig DELETE Config/:configId
delDocumentFolder DELETE DocFolders/:objType/:objId/:folderId delDocumentFolder DELETE DocFolder/:objectType/:objectId/:folderId
delDocumentLink DELETE DocLinks/:objType/:objId/:linkId delDocumentLink DELETE DocLink/:objectType/:objectId/:linkId
delEnumTypeTranslated DELETE Enum/:typeId/Type/WithTranslations delEnumTypeTranslated DELETE EnumType/:typeId/WithTranslations
delEnumValueTranslated DELETE Enum/:typeId/Value/:valueId/WithTranslations delEnumValueTranslated DELETE Enum/:typeId/Value/:valueId/WithTranslations
delExplicitRelationTypeTranslated DELETE ExplicitRelationType/:relationType/WithTranslations delExplicitRelationTypeTranslated DELETE ExplicitRelationType/:relationType/WithTranslations
delUniqueReadableCodeDef DELETE UniqueReadableCodeDef/:typeId delUniqueReadableCodeDef DELETE UniqueReadableCodeDef/:typeId
getAttachment GET Attachment/:attachmentId getAttachment GET Attachment/:attachmentId
getAttachmentBinary JS only getAttachmentBinary JS only
getAttachmentDetails GET Attachment/:attachmentId/Details getAttachmentDetails GET Attachment/:attachmentId/Details
getAttachmentJson JS only getAttachmentJson JS only
getAttachmentText JS only getAttachmentText JS only
getConfig GET Config/:configId getConfig GET Config/:configId
getCurrentTenant GET Tenant/Current getCurrentTenant GET Tenant/Current
getCurrentTenantLicenseFeatures GET Tenant/Current/License/Features getCurrentTenantLicenseFeatures GET Tenant/Current/License/Features
getDefaultIcon GET DefaultIcon/:objType getDefaultIcon GET DefaultIcon/:objectType
getNewWebSocketConnection POST WebSocket/Create createNewChangeNotificationWebSocket POST ChangeNotification/CreateNew
getPropertyDefinitionTypes GET ObjectTypes/WithDynamicProperties getAllPropertyObjectTypes GET PropertyObjectTypes
getRelationType GET RelationType/:relationType getRelationType GET RelationType/:relationType
getServerConfig GET ServerConfig/:configKey getServerConfig GET ServerConfig/:configKey
getUndeleteLimit GET UndeleteLimit getUndeleteLimit GET UndeleteLimit
getUniqueReadableCodeDef GET UniqueReadableCodeDef/:typeId getUniqueReadableCodeDef GET UniqueReadableCodeDef/:typeId
putConfig PUT Config/:configId putConfig PUT Config/:configId
putUniqueReadableCodeDef PUT UniqueReadableCodeDef/:typeId putUniqueReadableCodeDef PUT UniqueReadableCodeDef/:typeId

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
addAttachmentAndLink POST Attachment/:objType/:objId/Add addAttachmentAndLink POST DocLinks/:objectType/:objectId/Attach Parameter list changed
addAttachmentBinaryAndLink JS only addAttachmentBinaryAndLink JS only Parameter list changed
addAttachmentJsonAndLink JS only addAttachmentJsonAndLink JS only Parameter list changed
addAttachmentTextAndLink JS only addAttachmentTextAndLink JS only Parameter list changed
addDocumentFolder POST DocFolders/:objType/:objId/Add addDocumentFolder POST DocFolders/:objectType/:objectId/Add A folder inside another folder is added with addDocumentFolderSubFolder
convertUnit GET UnitSystem/:systemId/Convert convertUnit GET UnitSystem/:systemId/Convert Returns the ID of the unit (unitId) instead of the unit itself; read the unit with getUnit
delExplicitRelationByRef DELETE ExplicitRelations/:relationType/Delete removeExplicitRelation POST ExplicitRelations/:relationType/Remove HTTP method changed
getAllRelations GET Relations/OfObject/:objectType/:objectId/All getAllRelations GET Relations/OfObject/:objectType/:objectId/All Reports the relation type as the type of each relation; read it with getRelationTypes for its name and whether it is explicit
getAllRelationTypesForType GET RelationTypes/OfObjectType/:objectType/AllRelTypes getAllRelationTypesForType GET RelationTypes/OfObjectType/:objectType/All Returns references to the relation types; read them with getRelationTypes
getBackwardRelations GET Relations/OfObject/:targetType/:targetId/Backward getBackwardRelations GET Relations/OfObject/:targetType/:targetId/Backward Reports the relation type as the type of each relation; read it with getRelationTypes for its name and whether it is explicit
getBackwardRelationTypesForType GET RelationTypes/OfObjectType/:objectType/BackwardRelTypes getBackwardRelationTypesForType GET RelationTypes/OfObjectType/:objectType/Backward Returns references to the relation types; read them with getRelationTypes
getCurrentTenantWithAdditionalData GET Tenant/Current/WithAdditionalData getCurrentTenantWithAdditionalData GET Tenant/Current/WithAdditionalData Response body changed
getCurrentUserAllRelations GET Relations/OfCurrentUser/All getCurrentUserAllRelations GET Relations/OfCurrentUser/All Reports the relation type as the type of each relation; read it with getRelationTypes for its name and whether it is explicit
getCurrentUserBackwardRelations GET Relations/OfCurrentUser/Backward getCurrentUserBackwardRelations GET Relations/OfCurrentUser/Backward Reports the relation type as the type of each relation; read it with getRelationTypes for its name and whether it is explicit
getCurrentUserForwardRelations GET Relations/OfCurrentUser/Forward getCurrentUserForwardRelations GET Relations/OfCurrentUser/Forward Reports the relation type as the type of each relation; read it with getRelationTypes for its name and whether it is explicit
getDocumentFolder GET DocFolders/:objType/:objId/:folderId getDocumentFolder GET DocFolder/:objectType/:objectId/:folderId Response body changed
getDocumentFolderParent GET DocFolder/:folderId/Parent getDocumentFolderParent GET DocFolderParent/:folderId Response body changed
getDocumentFolders GET DocFolders/:objType/:objId getAllDocumentFolders GET DocFolders/:objectType/:objectId Parameter list changed
getDocumentLink GET DocLinks/:objType/:objId/:linkId getDocumentLink GET DocLink/:objectType/:objectId/:linkId Response body changed
getDocumentLinkParent GET DocLink/:linkId/Parent getDocumentLinkParent GET DocLinkParent/:linkId Response body changed
getDocumentLinks GET DocLinks/:objType/:objId getAllDocumentLinks GET DocLinks/:objectType/:objectId Parameter list changed
getEnumType GET Enum/:typeId/Type getEnumType GET EnumType/:typeId Response body changed
getEnumTypes GET Enum/Types getAllEnumTypes GET EnumTypes Returns references; read them with getEnumTypes
getEnumTypeTranslated GET Enum/:typeId/Type/WithTranslations getEnumTypeTranslated GET EnumType/:typeId/WithTranslations Response body changed
getEnumValue GET Enum/:typeId/Value/:valueId getEnumValue GET Enum/:typeId/Value/:valueId Response body changed
getEnumValues GET Enum/:typeId/Values getAllEnumValues GET Enum/:typeId/Values Returns references; read them with getEnumValues; deleted is gone
getEnumValuesTranslated GET Enum/:typeId/Values/WithTranslations getAllEnumValues GET Enum/:typeId/Values Returns references; read them with getEnumValuesTranslated
getEnumValueTranslated GET Enum/:typeId/Value/:valueId/WithTranslations getEnumValueTranslated GET Enum/:typeId/Value/:valueId/WithTranslations Response body changed
getExplicitRelation GET ExplicitRelation/:relationType/:relationId getExplicitRelation GET ExplicitRelation/:relationType/:relationId Response body changed — V3 Relation carries only type, id, source and target. explicit and name are gone, and relationType has no successor because type now holds the relation type itself, where V2 put a fixed Relation there
getExplicitRelationTypesTranslated GET ExplicitRelationTypes/WithTranslations getAllRelationTypes GET RelationTypes Returns references; pass implicit=false, then read them with getExplicitRelationTypesTranslated
getExplicitRelationTypeTranslated GET ExplicitRelationType/:relationType/WithTranslations getExplicitRelationTypeTranslated GET ExplicitRelationType/:relationType/WithTranslations Response body changed
getForwardRelations GET Relations/OfObject/:sourceType/:sourceId/Forward getForwardRelations GET Relations/OfObject/:sourceType/:sourceId/Forward Reports the relation type as the type of each relation; read it with getRelationTypes for its name and whether it is explicit
getForwardRelationTypesForType GET RelationTypes/OfObjectType/:objectType/ForwardRelTypes getForwardRelationTypesForType GET RelationTypes/OfObjectType/:objectType/Forward Returns references to the relation types; read them with getRelationTypes
getMaxParallelRequests GET Limitations/MaxParallelRequests getMaxParallelRequests GET Limits/MaxParallelRequests Reports the limit as maxParallel
getPropertyDefinition GET PropertyDefinition/:definitionId getPropertyPresentation GET PropertyPresentation/:definitionId/OfClient/:clientId The client is part of the URL — pass Client4
getPropertyDefinitions GET ObjectType/:objectType/PropertyDefinitions getAllPropertyPresentations GET PropertyPresentations/OfClient/:clientId/OfObjectType/:objectType The client is part of the URL — pass Client4
getPropertyDefinitionsTranslated GET ObjectType/:objectType/PropertyDefinitions/WithTranslations getAllPropertyPresentationsTranslated GET PropertyPresentations/OfClient/:clientId/OfObjectType/:objectType/WithTranslations The client is part of the URL — pass Client4
getRelationTypes GET RelationTypes getAllRelationTypes GET RelationTypes Returns references; read them with getRelationTypes
getUniqueReadableCodeDefs GET UniqueReadableCodeDefs getAllUniqueReadableCodeDefs GET UniqueReadableCodeDefs Response body changed
getUnit GET UnitSystem/:systemId/Unit/:unitId getUnit GET UnitSystem/:systemId/Unit/:unit Names the member abbr abbreviation
getUnits GET UnitSystem/:systemId/Units getUnits GET UnitSystem/:systemId/Units Names the member abbr abbreviation
getUnitsFactors GET UnitSystem/:systemId/Factors getUnitFactors GET UnitSystem/:systemId/Factors Names the unit unitId
getUnitsPreference GET UnitSystem/:systemId/Preference getUnitPreference GET UnitSystem/:systemId/Preference Names the preferred units preferredUnitIds
moveDocumentFolder POST DocFolders/:objType/:objId/:folderId/Move moveDocumentFolder + moveDocumentFolderToTop POST DocFolder/:objType/:objId/:folderId/MoveTo, POST DocFolder/:objType/:objId/:folderId/MoveToTop Two endpoints now: the destination folder is mandatory on MoveTo, and moving to the top has its own call instead of an omitted folder
moveDocumentLink POST DocLinks/:objType/:objId/:linkId/Move moveDocumentLink + moveDocumentLinkToTop POST DocLink/:objType/:objId/:linkId/MoveTo, POST DocLink/:objType/:objId/:linkId/MoveToTop Two endpoints now: the destination folder is mandatory on MoveTo, and moving to the top has its own call instead of an omitted folder
putDocumentFolder PUT DocFolders/:objType/:objId/:folderId putDocumentFolder PUT DocFolder/:objectType/:objectId/:folderId Request body changed
putDocumentLink PUT DocLinks/:objType/:objId/:linkId putDocumentLink PUT DocLink/:objectType/:objectId/:linkId Request body changed
putEnumTypeTranslated PUT Enum/:typeId/Type/WithTranslations putEnumTypeTranslated PUT EnumType/:typeId/WithTranslations Takes the V3 IdentificationTranslated as its body
putEnumValueTranslated PUT Enum/:typeId/Value/:valueId/WithTranslations putEnumValueTranslated PUT Enum/:typeId/Value/:valueId/WithTranslations Takes the V3 IdentificationTranslated as its body
putExplicitRelationTypeTranslated PUT ExplicitRelationType/:relationType/WithTranslations putExplicitRelationTypeTranslated PUT ExplicitRelationType/:relationType/WithTranslations Takes the V3 ExplicitRelationTypeTranslated as its body
putPropertyDefinitionsTranslated PUT ObjectType/:objectType/PropertyDefinitions/WithTranslations putAllPropertyPresentationsTranslated + putAllPropertyDefinitions PUT PropertyPresentations/OfClient/:clientId/OfObjectType/:objectType/WithTranslations, PUT PropertyDefinitions/OfObjectType/:objectType Two calls now: the definitions and the client's presentations of them are written separately
putUnitsFactors PUT UnitSystem/:systemId/Factors putUnitFactors PUT UnitSystem/:systemId/Factors Body names the unit unitId
putUnitsPreference PUT UnitSystem/:systemId/Preference putUnitPreference PUT UnitSystem/:systemId/Preference Body names the preferred units preferredUnitIds

Retired — no V3 successor; the endpoint disappears with API V2.

V2 endpoint V2 path Note
delEnumValuesTranslated DELETE Enum/:typeId/Values/WithTranslations Use single delete function, will be discontinued at end of 2028
delExplicitRelation DELETE ExplicitRelation/:relationType/:relationId delete relations by reference, will be discontinued at end of 2028
getDataMatrix GET Barcode/DataMatrix Will not be transferred to V3 and will be discontinued at end of 2028
getQrCode GET Barcode/QR Will not be transferred to V3 and will be discontinued at end of 2028
getUserAgent GET UserAgent Will not be transferred to V3 and will be discontinued at end of 2028
inspectConnection POST Connection/Inspect Will not be transferred to V3 and will be discontinued at end of 2028
putEnumValues PUT Enum/:typeId/Values This endpoint is deprecated since 4.06.0 and will be discontinued at end of 2028. Use PUT /API/V2/PF/Enum/:typeId/Values/WithTranslations instead
putEnumValuesTranslated PUT Enum/:typeId/Values/WithTranslations Use single put function, will be discontinued at end of 2028

New in V3 — no V2 equivalent.

addDocumentFolderAttachmentAndLink, addDocumentFolderAttachmentBinaryAndLink, addDocumentFolderAttachmentJsonAndLink, addDocumentFolderAttachmentTextAndLink, addDocumentFolderDocumentLink, addDocumentFolderSubFolder, addDocumentLink, delPropertyDefinition, delPropertyPresentationTranslated, getAllDocumentFoldersOfFolder, getAllDocumentLinksOfFolder, getAllPropertyDefinitions, getDocumentFolders, getDocumentFoldersParents, getDocumentLinks, getDocumentLinksParents, getEnumTypesTranslated, getIcon, getPropertyDefinition, getPropertyPresentationTranslated, putPropertyDefinition, putPropertyPresentationTranslated

PI — Project items

  • addProjectItemByRef folds into addProjectItem, which now takes relType / refType / refId (and forward) as optional parameters. The redirect handles it, so this is a URL change only.
  • getProjectItemSummariesByRef folds into getProjectItemsByRef — one endpoint, one shape.
  • Watch the plural type getter. V2's getProjectItemTypes returned all item types; in V3 that is getAllProjectItemTypes, and the name getProjectItemTypes now means "the types with these IDs". Same name, different meaning.
  • addProjectItem takes colorId (a reference into a colour enum) where most modules take color.
  • New: item identifications, a version timeline and historic versions.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
addProjectItem POST ItemType/:type/Add addProjectItem POST ItemType/:type/Items/Add
addProjectItemByRef POST ItemType/:type/ByRef/:reltype/:reftype/:refid/Add addProjectItem POST ItemType/:type/Items/Add
delProjectItem DELETE ItemType/:type/Item/:itemId delProjectItem DELETE ItemType/:itemType/Item/:projectItemId

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getProjectItem GET ItemType/:type/Item/:itemId getProjectItem GET ItemType/:itemType/Item/:projectItemId No longer includes the lifecycle state; read it with lcy.getObjectState
getProjectItems GET ItemType/:type/Items getAllProjectItems GET ItemType/:type/Items Returns references; state becomes states, lateststatechange becomes reachedAtOrAfter and requires states. Read the items with getProjectItems
getProjectItemsByRef GET ItemType/:type/ByRef/:reltype/:reftype/:refid/Items getProjectItemsByRef GET ItemType/:type/Ref/:relType/:refType/:refId/Items Parameter list changed
getProjectItemSummariesByRef GET ItemType/:type/ByRef/:reltype/:reftype/:refid/ItemSummaries getProjectItemsByRef GET ItemType/:type/Ref/:relType/:refType/:refId/Items Parameter list changed
getProjectItemType GET ItemType/:type getProjectItemType GET ItemType/:type Response body changed
getProjectItemTypes GET ItemTypes getAllProjectItemTypes GET ItemTypes Response body changed
putProjectItem PUT ItemType/:type/Item/:itemId putProjectItem PUT ItemType/:itemType/Item/:projectItemId Takes the V3 ProjectItem as its body

New in V3 — no V2 equivalent.

getProjectItemIdentification, getProjectItemIdentifications, getProjectItemTimeline, getProjectItemTypes, getProjectItemVersion

PM — Project management (projects, portfolios, reports, scenarios, work items)

The largest module, and fully migrated — every endpoint has a V3 successor or is retired.

  • Read section 6 first. The work-item object type is now ProjectWorkItem, and five V3 endpoint names were changed to spell the object out in full.
  • getProjects changed meaning. V2's getProjects returned every project in full. Use getAllProjects for the reference list and getProjects/:projectIds for a page of details (section 5.2). The same applies to getProjectIdentifications, and to portfolios, reports and scenarios.
  • getProjectDetails / putProjectDetails are retired: the object endpoints carry the same data.
  • Report indicators are a three-layer model in V3, addressable layer by layer. The tenant-wide definition, the selection with a target on a project and the target/actual comparison on a report each have their own endpoints, and each is split into a dimension and a detail - see docs/pm-indicator-model.md. Two consequences for a V2 caller: a selection is now addressed by its own ID, not by the ID of the indicator definition it realises, and the whole-collection putIndicatorSelections is replaced by one call per object. The two delIndicatorIntl* endpoints redirect, and in doing so lose the :projectReportTypeId segment V2 used to check the definition against - a call naming the wrong report type used to do nothing and now deletes.
  • A definition is written in every language at once. IndicatorDetail, IndicatorDimension, IndicatorClassification and IndicatorClassificationValue are read in the user's language but written only through .../WithTranslations, with name, description and iconRef as translation arrays - the same shape putIndicatorIntlDetailDef and putIndicatorIntlDimensionDef use in V2, so a write can no longer drop the translations it did not send. All four translated bodies are pf.BasicObjectTranslateds, so they also report version and the create/modify audit data; those members are server-managed and ignored on write. There is no single-language PUT on any of the four, and for the same reason no Identification endpoint on them. A classification scheme is flat in V3: its body no longer carries the steps, and a step is an addressable object of its own - IndicatorClassificationValue, read one by one or as references under the scheme it belongs to. V2's putIndicatorIntlClassification and delIndicatorIntlClassification are gone: neither ever had a server-side implementation, every call answered 500, so no integration can have depended on them. Write a scheme through pm.putIndicatorClassificationTranslated and each of its steps through pm.putIndicatorClassificationValueTranslated.
  • The latest report is a reference now, and one per parent. getLatestProjectReport and getLatestProjectPortfolioReport become getLatestProjectReportOfProject and getLatestProjectPortfolioReportOfProjectPortfolio, each with a bulk sibling taking several parent IDs. They answer a pf.ReferenceWithParent instead of the full report - read it with getProjectReport / getProjectPortfolioReport. The report type leaves the path and becomes the optional reportTypeFilter; with several types the answer is the latest report of any of them, not one per type. state becomes stateFilter, skip stays. The two ...WithLatestReports endpoints have no single successor: list the parents with getAllProjects / getAllProjectPortfolios, call the bulk once per report type of interest, and read the reports with getProjectReports / getProjectPortfolioReports. Their misspelled twin getAllProjectPortfoliosWithLatestReportsDeprecated follows the same recipe.
  • Reports of a project or portfolio are references too. getProjectReports and getProjectReportsByType become getProjectReportsOfProject, getProjectPortfolioReports and getProjectPortfolioReportsByType become getProjectPortfolioReportsOfProjectPortfolio; the report type is always the optional reportTypeFilter. The V3 endpoints of the old names, getProjectReports / getProjectPortfolioReports, are the bulk reads by report ID that turn those references into full reports.
  • The sub-work-item pair moved and changed shape. addWorkItemSubWorkItem becomes addProjectWorkItemSubWorkItem on ProjectWorkItem/:workItemId/SubWorkItems/Add, and it no longer takes a request body: pass name, code, description, color, iconRef, start, end and sortIndex as query parameters instead (section 5.8). getWorkItemSubWorkItems becomes getSubWorkItemsOfProjectWorkItem on ProjectWorkItem/:workItemId/SubWorkItems, takes depth and sort instead of version, and returns references rather than full work items — read the ones you need with getProjectWorkItems.
  • Portfolio scenarios are PortfolioSimulation in V3, and the two names are not interchangeable. The object type was renamed from PM-PFSCENARIO to PortfolioSimulation; V2 keeps reporting the old value, V3 reports the new one, and a reference read from one API must never be compared with one read from the other. For the same reason the four endpoints that carry a body — getPortfolioScenarios, getPortfolioScenario, putPortfolioScenario and addPortfolioScenario — are not redirected to V3: a <redirectV3> is a real 307, so the client would receive the V3 body with the V3 type and bypass the V2 translation entirely. They stay V2-implemented until V2 reaches end of life. delPortfolioScenario is redirected: it has no body in either direction, so there is no type for the caller to read off it.
  • Portfolio milestones are a V3 object family now, addressed by their own ID. V2 reached a milestone through its portfolio (ProjectPortfolio/:portfolioId/Milestone/:milestoneId); V3 addresses it as ProjectPortfolioMilestone/:projectPortfolioMilestoneId, with the usual bulk, identification and delete endpoints beside it. addProjectPortfolioMilestone and delProjectPortfolioMilestone are redirected — the delete now ignores the portfolio the V2 URL carried and deletes by milestone ID, so a call that named the wrong portfolio used to fail with 400 and now succeeds. add takes the milestone position as position instead of at, and it is mandatory: a milestone has to sit somewhere on the time axis. It also accepts the basic object properties, and names the new milestone after its type when name is omitted. getProjectPortfolioMilestone and putProjectPortfolioMilestone are not redirected: the V3 body is a CustomizedObject and no longer carries validityStart / validityUntil. V2's getProjectPortfolioMilestones listed one portfolio's milestones in full; in V3 that name is the bulk getter, and the list is getProjectPortfolioMilestonesOfProjectPortfolio (references, includeParentPortfolios instead of includeparent) or getAllProjectPortfolioMilestones tenant-wide.
  • Dependencies are addressed by their own ID now, and the ends are plain references. V2 reached a dependency under Dependency/:dependencyId and answered a body that spelled out each end - its name, position, color, milestone kind and the chain of parents above it - plus a constraintType that was always MANUAL. V3 keeps predecessor and successor as type-and-ID references and drops the rest: read the ends with getProjectMilestone(s) resp. getProjectPortfolioMilestone(s), and go from ProjectMilestone.workItemId to getProjectWorkItem for the task, its scenario and its project. addProjectDependency and delProjectDependency are redirected; add takes the two ends in camel case (predecessorType, predecessorId, successorType, successorId), all four mandatory and restricted to ProjectMilestone and ProjectPortfolioMilestone, refuses an end that does not exist with 404, and now also accepts minDistance and maxDistance. All four lists - tenant-wide, per project, per portfolio and per scenario - are getAll* and answer references, each one naming the scenarios and portfolios the dependency sits in, and all four take a sort; read the dependencies themselves with getProjectDependencies/:ids. The version parameter is gone from all of them - V3 has no historic read for a dependency.
  • Constraints between the milestones of one scenario are addressed by their own ID, and the scenario keeps the create. V2 reached a constraint under ProjectScenario/:scenarioId/Constraint/:constraintId; V3 calls the object a ProjectMilestoneConstraint and addresses it by its ID alone, reading the permission off the project scenario behind it - READ to read, UPDATE to write - exactly as V2 did off the scenario in the URL. PUT no longer creates: addProjectScenarioMilestoneConstraint does, under the scenario, with a server-generated ID, and it answers a reference. putProjectMilestoneConstraint changes the distances of a constraint that exists; its two ends and its constraintType are fixed, and a body that names other milestones is refused with 422. The movetarget / movesource parameters are gone from the write and became an action of their own, fixProjectMilestoneConstraint, which takes move=PREDECESSOR|SUCCESSOR (and the resourceId for workdays). The four mutually exclusive filters of getProjectConstraints became three lists - per scenario, per work item and per milestone, the last with an optional direction - which answer references naming the two milestones each constraint connects; read the constraints themselves with getProjectMilestoneConstraints/:ids. The advancedtaskdependencies license is enforced by the API now: on add it is the presence of minDistance or maxDistance that needs it, so passing minDistance=0 without the license fails where V2 allowed it.
  • The two position proposals keep their URL and their behaviour, but are not redirected. proposeWorkItemEnd and proposeWorkItemStart become proposeProjectWorkItemEnd and proposeProjectWorkItemStart - the work item is spelled in full, as everywhere else on this resource - and maxmove is spelled maxMove. The path, the bounds (720 >= maxMove > 0), the optimizetask license and the body are unchanged, except that V3 names the milestone milestoneId where V2 says objectId. One answer is not: where V2 replied 200 with a JSON null - a work item that is not a phase, that has no such end, or that no resource is allocated to, so that there is no workload to optimize - V3 replies 404, the same corner case the move-pattern endpoints have.
  • The move-pattern endpoints are not redirected. getMovePatternByWorkItem, getMovePatternByWorkItemStart and getMovePatternByWorkItemEnd become getProjectWorkItemMovePattern, getProjectWorkItemStartMovePattern and getProjectWorkItemEndMovePattern; URL and parameters are unchanged, the payload names the milestone milestoneId where V2 says objectId. One answer differs as well: where V2 replied 200 with a JSON null — a work item without milestones of its own, or one that cannot move at all — V3 replies 404.
  • delMicroAllocations moved to ALC and is redirected there. Clearing the reservations a work item holds in the plan of one resource is a write on that resource's shift plan, so V3 spells it alc.unassignResourceShiftPlanByProjectWorkItem, POST ALC/Resource/:resourceId/ShiftPlan/ByProjectWorkItem/:projectWorkItemId/Delete, where V2 said POST ProjectWorkItem/:workItemId/DelMicroAllocations?res=.... It deletes the same thing - every reservation of that work item on that resource, over the whole time the plan spans - and leaves the absences alone, and it still demands UPDATE on the work item. Two things differ: the second permission is RESERVE on the resource where V2 asked for PLAN, which is what every other write of reservations in V3 asks for; and a call that omits the mandatory res runs into 404 at the V3 URL the redirect points at, where V2 answered 400.
  • The shift plan of a work item keeps its URL, but the call that assigns it moved to ALC. The three calls on the plan itself become getProjectWorkItemShiftPlan, putProjectWorkItemShiftPlan and delProjectWorkItemShiftPlan - the work item is spelled in full, as everywhere else on this resource - and their paths stay as they were. What the plan means is unchanged: one pattern per resource shift of the tenant, as long as the work item is, counted from its start, and writing it reserves nothing. The delete is redirected; the read and the write are not, because a pattern names its shift by ID now: shift, a whole embedded res.ResourceShift, became shiftId, matching alc.ResourceShiftPlanRow. Read the shifts themselves with res.getResourceShifts if the names, colors or times are needed. assignWorkItemShiftPlan is the odd one out: laying the pattern onto a resource is a write on that resource's shift plan, so it joined its inverse unassignResourceShiftPlanByProjectWorkItem in ALC as assignResourceShiftPlanByProjectWorkItem - see the ALC section above for what changed with it.
  • Macro allocations moved to ALC as resource allocations. The seven V2 endpoints on MacroAllocation are reimplemented in ALC - see the ALC section for the new object and what changed with it.
  • New capability worth adopting: identifications, timelines and historic versions for projects, portfolios, reports, scenarios and work items; the report-type lists; the milestone endpoints; the scenario lookups (getScenariosOfProject, getActiveScenarioOfProjects); and getProjectReportRecalculated.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
addProjectDependency POST Dependency/Add addProjectDependency POST ProjectDependencies/Add
addProjectPortfolio POST ProjectPortfolios/Add addProjectPortfolio POST ProjectPortfolios/Add
addProjectPortfolioMilestone POST ProjectPortfolio/:portfolioId/Milestones/Add addProjectPortfolioMilestone POST ProjectPortfolio/:projectPortfolioId/Milestones/Add
addProjectPortfolioProject POST ProjectPortfolio/:portfolioId/Projects/Add addProjectPortfolioProject POST ProjectPortfolio/:projectPortfolioId/Projects/Add
addProjectPortfolioReport POST ProjectPortfolio/:projectPortfolioId/AddReport/:reportType addProjectPortfolioReport POST ProjectPortfolio/:projectPortfolioId/Reports/Add/:reportType
addProjectPortfolioSubPortfolio POST ProjectPortfolio/:portfolioId/SubPortfolios/Add addProjectPortfolioSubPortfolio POST ProjectPortfolio/:projectPortfolioId/SubPortfolios/Add
addProjectReport POST Project/:projectId/AddReport/:reportType addProjectReport POST Project/:projectId/Reports/Add/:reportType
addProjectScenario POST Project/:projectId/AddScenario addProjectScenario POST Project/:projectId/Scenarios/Add
assignWorkItemShiftPlan POST ProjectWorkItem/:workItemId/ShiftPlan/Assign alc.assignResourceShiftPlanByProjectWorkItem POST ALC/Resource/:resourceId/ShiftPlan/ByProjectWorkItem/:projectWorkItemId/Assign
delIndicatorIntlDetailDef DELETE Indicators/:projectReportTypeId/Detail/IntlDefinition/:detailId delIndicatorDetailTranslated DELETE IndicatorDetail/:indicatorDetailId/WithTranslations
delIndicatorIntlDimensionDef DELETE Indicators/:projectReportTypeId/Dimension/IntlDefinition/:dimensionId delIndicatorDimensionTranslated DELETE IndicatorDimension/:indicatorDimensionId/WithTranslations
delMacroAllocation DELETE MacroAllocation/:macroAllocationId alc.delResourceAllocation DELETE ALC/ResourceAllocation/:resourceAllocationId
delMicroAllocations POST ProjectWorkItem/:workItemId/DelMicroAllocations alc.unassignResourceShiftPlanByProjectWorkItem POST ALC/Resource/:resourceId/ShiftPlan/ByProjectWorkItem/:projectWorkItemId/Delete
delPmMethodPhaseTranslated DELETE Method/:methodId/Phase/:phaseId/WithTranslations delProjectManagementMethodologyPhaseTranslated DELETE Methodology/:methodId/Phase/:phaseId/WithTranslations
delPmMethodTranslated DELETE Method/:methodId/WithTranslations delProjectManagementMethodologyTranslated DELETE Methodology/:methodId/WithTranslations
delPortfolioScenario DELETE PortfolioScenario/:pfScenarioId delPortfolioSimulation DELETE PortfolioSimulation/:portfolioSimulationId
delProject DELETE Project/:projectId delProject DELETE Project/:projectId
delProjectConstraint DELETE ProjectScenario/:scenarioId/Constraint/:constraintId delProjectMilestoneConstraint DELETE ProjectMilestoneConstraint/:projectMilestoneConstraintId
delProjectDependency DELETE Dependency/:dependencyId delProjectDependency DELETE ProjectDependency/:projectDependencyId
delProjectPortfolio DELETE ProjectPortfolio/:portfolioId delProjectPortfolio DELETE ProjectPortfolio/:projectPortfolioId
delProjectPortfolioMilestone DELETE ProjectPortfolio/:portfolioId/Milestone/:milestoneId delProjectPortfolioMilestone DELETE ProjectPortfolioMilestone/:projectPortfolioMilestoneId
delProjectPortfolioReport DELETE ProjectPortfolioReport/:reportType/:reportId delProjectPortfolioReport DELETE ProjectPortfolioReport/:reportType/:projectPortfolioReportId
delProjectPortfolioReportTypeTranslated DELETE ProjectPortfolioReportType/:reportTypeId/WithTranslations delProjectPortfolioReportTypeTranslated DELETE ProjectPortfolioReportType/:projectPortfolioReportTypeId/WithTranslations
delProjectReport DELETE ProjectReport/:reportType/:reportId delProjectReport DELETE ProjectReport/:reportType/:projectReportId
delProjectReportTypeTranslated DELETE ProjectReportType/:reportTypeId/WithTranslations delProjectReportTypeTranslated DELETE ProjectReportType/:projectReportTypeId/WithTranslations
delProjectScenario DELETE ProjectScenario/:scenarioId delProjectScenario DELETE ProjectScenario/:projectScenarioId
delWorkItem DELETE ProjectWorkItem/:workItemId delProjectWorkItem DELETE ProjectWorkItem/:projectWorkItemId
delWorkItemLog DELETE ProjectWorkItem/:workItemId/Log/:logId delProjectWorkItemLog DELETE ProjectWorkItemLog/:projectWorkItemLogId
delWorkItemShiftPlan DELETE ProjectWorkItem/:workItemId/ShiftPlan delProjectWorkItemShiftPlan DELETE ProjectWorkItem/:projectWorkItemId/ShiftPlan
downloadProjectScenario GET ProjectScenario/:scenarioId/Download exportProjectScenario GET ProjectScenario/:projectScenarioId/Export
getPmMethodPhaseLifecycleAssignment GET Method/:methodId/PhaseLifecycleAssignments getProjectManagementMethodologyPhaseLifecycleAssignments GET Methodology/:methodId/PhaseLifecycleAssignments
getPmMethodsPhaseLifecycleAssignment GET Methods/PhaseLifecycleAssignments getAllProjectManagementMethodologyPhaseLifecycleAssignments GET MethodologyPhaseLifecycleAssignments
linkProject POST Project/:projectId/LinkTo linkProject POST Project/:projectId/Link
moveProject POST Project/:projectId/MoveTo moveProject POST Project/:projectId/MoveTo
moveProjectPortfolio POST ProjectPortfolio/:portfolioId/MoveTo moveProjectPortfolio POST ProjectPortfolio/:projectPortfolioId/MoveTo
moveProjectPortfolioToTop POST ProjectPortfolio/:portfolioId/MoveToTop moveProjectPortfolioToTop POST ProjectPortfolio/:projectPortfolioId/MoveToTop
moveWorkItem POST ProjectWorkItem/:workItemId/Move moveProjectWorkItem POST ProjectWorkItem/:projectWorkItemId/Move
moveWorkItemEnd POST ProjectWorkItem/:workItemId/End/Move moveProjectWorkItemEnd POST ProjectWorkItem/:projectWorkItemId/End/Move
moveWorkItemStart POST ProjectWorkItem/:workItemId/Start/Move moveProjectWorkItemStart POST ProjectWorkItem/:projectWorkItemId/Start/Move
putPmMethodsPhaseLifecycleAssignment PUT Methods/PhaseLifecycleAssignments putProjectManagementMethodologyPhaseLifecycleAssignments PUT MethodologyPhaseLifecycleAssignments
unlinkProject DELETE Project/:projectId/LinkTo unlinkProject DELETE Project/:projectId/Link
uploadProjectPortfolioProject POST ProjectPortfolio/:portfolioId/Projects/Upload importProjectPortfolioProject POST ProjectPortfolio/:projectPortfolioId/Projects/Import
uploadProjectScenario POST Project/:projectId/UploadScenario importProjectScenario POST Project/:projectId/Scenarios/Import

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
addPortfolioScenario POST PortfolioScenarios/Add addPortfolioSimulation POST PortfolioSimulations/Add Optional name, code, description, color and iconRef added
addWorkItemMacroAllocation POST ProjectWorkItem/:workItemId/MacroAllocations/Add alc.addResourceAllocation POST ALC/ProjectWorkItem/:projectWorkItemId/ResourceAllocations/Add argid becomes resourceId and amountType takes the V3 values; a resource already allocated to the work item is answered with 400 instead of being overwritten
addWorkItemSubWorkItem POST ProjectWorkItem/:workItemId/AddChild addProjectWorkItemSubWorkItem POST ProjectWorkItem/:projectWorkItemId/SubWorkItems/Add Parameter list changed
copyWorkItemTree POST ProjectWorkItem/:workItemId/Copy copyProjectWorkItemTree POST ProjectWorkItem/:projectWorkItemId/CopyTo/:targetId 2nd parameter changed
delProjectWorkItemProgressCorrection DELETE ProjectWorkItem/:workItemId/ProgressCorrection/At delProjectWorkItemProgressCorrection DELETE ProjectWorkItem/:projectWorkItemId/ProgressCorrection/At/:day The day is a mandatory path segment instead of the optional at (which defaulted to today, UTC); deleteoverride is spelled deleteOverride
delWorkItemMacroAllocation DELETE ProjectWorkItem/:workItemId/MacroAllocations/Delete alc.delResourceAllocation DELETE ALC/ResourceAllocation/:resourceAllocationId Addressed by the allocation's ID instead of work item and resource; find it with alc.getAllResourceAllocationsOfProjectWorkItem
getAllProjectDependencies GET Dependencies getAllProjectDependencies GET ProjectDependencies Returns references with the objects each dependency sits in; the V3 name getProjectDependencies is the bulk getter
getAllProjectPortfolioReports GET ProjectPortfolioReports/:reportType getAllProjectPortfolioReports GET ProjectPortfolioReports Returns references to all project portfolio reports; read them with getProjectPortfolioReports; the state filter is not available
getAllProjectPortfoliosWithLatestReports GET ProjectPortfolios/WithLatestReports getLatestProjectPortfolioReportOfProjectPortfolios GET ProjectPortfolios/:projectPortfolioIds/LatestReport Takes the portfolio IDs (list them with getAllProjectPortfolios) and answers one reference per portfolio; call it once per report type and read the reports with getProjectPortfolioReports
getAllProjectPortfoliosWithLatestReportsDeprecated GET ProjectPortfolioss/WithLatestReports getLatestProjectPortfolioReportOfProjectPortfolios GET ProjectPortfolios/:projectPortfolioIds/LatestReport Misspelled twin of getAllProjectPortfoliosWithLatestReports, same successor: takes the portfolio IDs (list them with getAllProjectPortfolios) and answers one reference per portfolio; call it once per report type and read the reports with getProjectPortfolioReports
getAllProjectReports GET ProjectReports/:reportType getAllProjectReports GET ProjectReports Returns references to all project reports; read them with getProjectReports; the state filter and the project risks of a report are not available
getAllProjectsWithLatestReports GET Projects/WithLatestReports getLatestProjectReportOfProjects GET Projects/:projectIds/LatestReport Takes the project IDs (list them with getAllProjects) and answers one reference per project; call it once per report type and read the reports with getProjectReports
getIndicatorClassification GET Indicators/Classification/:classificationId getIndicatorClassification GET IndicatorClassification/:indicatorClassificationId Response gains type and id and no longer carries values - read the steps of a scheme with getAllIndicatorClassificationValuesByClassification
getIndicatorClassificationValue GET Indicators/ClassificationValue/:classificationId getIndicatorClassificationValue GET IndicatorClassificationValue/:indicatorClassificationValueId Response gains type, id and indicatorClassificationId
getIndicatorClassifications GET Indicators/Classifications getAllIndicatorClassifications GET IndicatorClassifications Returns references, not whole schemes; read one with getIndicatorClassification
getIndicatorDetailDef GET Indicators/:projectReportTypeId/Detail/Definition/:detailId getIndicatorDetail GET IndicatorDetail/:indicatorDetailId Addressed without the report type. Response renames dimension to indicatorDimensionId, adds source, and drops valueType, inputActual, inputRemaining, inputForecast, accumulation and projectManagementMethodPhase
getIndicatorDetailDefs GET Indicators/:projectReportTypeId/Detail/Definitions getAllProjectReportTypeIndicatorDetails GET ProjectReportType/:projectReportTypeId/IndicatorDetails Returns references, not whole definitions
getIndicatorDimensionDef GET Indicators/:projectReportTypeId/Dimension/Definition/:dimensionId getIndicatorDimension GET IndicatorDimension/:indicatorDimensionId Addressed without the report type; response gains type and id
getIndicatorDimensionDefs GET Indicators/:projectReportTypeId/Dimension/Definitions getAllProjectReportTypeIndicatorDimensions GET ProjectReportType/:projectReportTypeId/IndicatorDimensions Returns references, not whole definitions
getIndicatorIntlClassification GET Indicators/IntlClassification/:classificationId getIndicatorClassificationTranslated GET IndicatorClassification/:indicatorClassificationId/WithTranslations The translation arrays are unchanged; the body gains version and the create/modify audit data and no longer carries values - the steps are read with getIndicatorClassificationValueTranslated
getIndicatorIntlDetailDef GET Indicators/:projectReportTypeId/Detail/IntlDefinition/:detailId getIndicatorDetailTranslated GET IndicatorDetail/:indicatorDetailId/WithTranslations Addressed without the report type. Response renames dimension to indicatorDimensionId and aggregationRule to source, and adds semantics, version and the create/modify audit data; the translation arrays are unchanged
getIndicatorIntlDimensionDef GET Indicators/:projectReportTypeId/Dimension/IntlDefinition/:dimensionId getIndicatorDimensionTranslated GET IndicatorDimension/:indicatorDimensionId/WithTranslations Addressed without the report type; the body gains version and the create/modify audit data
getIndicatorSelectionDetail GET Project/:projectId/IndicatorSelection/:projectReportTypeId/Detail/:detid getProjectIndicatorDetail GET Project/:projectId/IndicatorDetail/:projectIndicatorDetailId Addressed by the selection's own ID instead of the indicator definition's; typeId becomes indicatorDetailId
getIndicatorSelectionDetailHistory GET Project/:projectId/IndicatorSelection/:projectReportTypeId/Detail/:detid/History getProjectIndicatorDetailVersion GET Project/:projectId/IndicatorDetail/:projectIndicatorDetailId/Version/:versionId One version per call, addressed by version ID, instead of a granularity-sampled range
getIndicatorSelectionDetailTimeline GET Project/:projectId/IndicatorSelection/:projectReportTypeId/Detail/:detid/Timeline getProjectIndicatorDetailTimeline GET Project/:projectId/IndicatorDetail/:projectIndicatorDetailId/Timeline Addressed by the selection's own ID; returns a VersionTimeline
getIndicatorSelections GET Project/:projectId/IndicatorSelections/:projectReportTypeId getProjectIndicatorDetailsOfProject GET Project/:projectId/IndicatorDetails Split per axis - getProjectIndicatorDimensionsOfProject returns the dimensions - and returns references instead of the nested aggregate
getLatestProjectPortfolioReport GET ProjectPortfolio/:projectPortfolioId/LatestReport/:reportType getLatestProjectPortfolioReportOfProjectPortfolio GET ProjectPortfolio/:projectPortfolioId/LatestReport Answers a reference - read the report with getProjectPortfolioReport; the type becomes the optional reportTypeFilter (several types: latest of any), state becomes stateFilter
getLatestProjectReport GET Project/:projectId/LatestReport/:reportType getLatestProjectReportOfProject GET Project/:projectId/LatestReport Answers a reference - read the report with getProjectReport; the type becomes the optional reportTypeFilter (several types: latest of any), state becomes stateFilter
getMacroAllocation GET MacroAllocation/:macroAllocationId alc.getResourceAllocation GET ALC/ResourceAllocation/:resourceAllocationId Response body changed; currency and version are gone
getMovePatternByWorkItem GET ProjectWorkItem/:workItemId/MovePattern getProjectWorkItemMovePattern GET ProjectWorkItem/:projectWorkItemId/MovePattern Names the milestone milestoneId
getMovePatternByWorkItemEnd GET ProjectWorkItem/:workItemId/End/MovePattern getProjectWorkItemEndMovePattern GET ProjectWorkItem/:projectWorkItemId/End/MovePattern Names the milestone milestoneId
getMovePatternByWorkItemStart GET ProjectWorkItem/:workItemId/Start/MovePattern getProjectWorkItemStartMovePattern GET ProjectWorkItem/:projectWorkItemId/Start/MovePattern Names the milestone milestoneId
getPmMethod GET Method/:methodId getProjectManagementMethodology GET Methodology/:methodId Response body changed
getPmMethodPhase GET Method/:methodId/Phase/:phaseId getProjectManagementMethodologyPhase GET Methodology/:methodId/Phase/:phaseId Response body changed
getPmMethodPhases GET Method/:methodId/Phases getAllProjectManagementMethodologyPhases GET Methodology/:methodId/Phases Parameter list changed
getPmMethodPhaseTranslated GET Method/:methodId/Phase/:phaseId/WithTranslations getProjectManagementMethodologyPhaseTranslated GET Methodology/:methodId/Phase/:phaseId/WithTranslations Response body changed
getPmMethods GET Methods getAllProjectManagementMethodologies GET Methodologies Parameter list changed
getPmMethodTranslated GET Method/:methodId/WithTranslations getProjectManagementMethodologyTranslated GET Methodology/:methodId/WithTranslations Response body changed
getPortfolioScenario GET PortfolioScenario/:pfScenarioId getPortfolioSimulation GET PortfolioSimulation/:portfolioSimulationId Response body changed, and the object type it reports differs — see the note above
getPortfolioScenarios GET PortfolioScenarios getAllPortfolioSimulations GET PortfolioSimulations Returns references only; read the ones you need with getPortfolioSimulations/:ids
getProject GET Project/:projectId getProject GET Project/:projectId Historic versions are read with getProjectTimeline and getProjectVersion, the lifecycle state with lcy.getObjectState, the classification with getProjectClassification and the portfolios with getAllProjects
getProjectAccumulatedValues GET Project/:projectId/AccumulatedValues getAccumulatedStructureOfProjectWorkItems, getAccumulatedProgressOfProjectWorkItems, getAccumulatedTimesOfProjectWorkItems, getAccumulatedCostsOfProjectWorkItems GET ProjectWorkItems/:projectWorkItemIds/AccumulatedValues/{Structure,Progress,Times,Costs} Split into four groups, each answering only the work items asked for: find the active scenario with getActiveScenarioOfProject and its work items with getProjectWorkItemsOfProjectScenario. For the project as a whole, getAccumulated…OfProject answers one object. The currency parameter is targetCurrencyId (Costs only); the costs forecasts are grouped like a costs overview
getProjectActiveScenario GET Project/:projectId/ActiveScenario getActiveScenarioOfProject GET Project/:projectId/ActiveScenario Response body changed
getProjectActiveScenarioHistory GET Project/:projectId/ActiveScenario/History getActiveScenarioTimelineOfProject GET Project/:projectId/ActiveScenario/Timeline Reports a reference timeline: slots of until / latestReference / changeCount instead of a flat list of items, and it takes limit, from and granularity like the other V3 timelines
getProjectClassification GET Project/:projectId/Classification getProjectClassification GET Project/:projectId/Classification Names the member description of the classification data remark
getProjectConstraint GET ProjectScenario/:scenarioId/Constraint/:constraintId getProjectMilestoneConstraint GET ProjectMilestoneConstraint/:projectMilestoneConstraintId Addressed by its own ID; createdBy / modifiedBy are type-and-ID references now
getProjectConstraints GET ProjectScenario/:scenarioId/Constraints getAllProjectMilestoneConstraintsOfProjectScenario GET ProjectScenario/:projectScenarioId/MilestoneConstraints Returns references; the workItemId and milestoneId filters became getAllProjectMilestoneConstraintsOfProjectWorkItem resp. …OfProjectMilestone (with direction in place of predecessorId / successorId), read them with getProjectMilestoneConstraints/:ids
getProjectDependencies GET Project/:projectId/Dependencies getAllProjectDependenciesOfProject GET Project/:projectId/Dependencies Renamed, returns references, no historic version
getProjectDependency GET Dependency/:dependencyId getProjectDependency GET ProjectDependency/:projectDependencyId Response body changed, no historic version
getProjectIdentification GET Project/:projectId/Identification getProjectIdentification GET Project/:projectId/Identification No longer includes the lifecycle state; read it with lcy.getObjectState
getProjectIdentifications GET Projects/Identification getAllProjects GET Projects Returns references; read them with getProjectIdentifications
getProjectManagementMethod GET Project/:projectId/ManagementMethod getProjectManagementMethodologyOfProject GET Project/:projectId/Methodology Response body changed
getProjectManagementMethodPhase GET Project/:projectId/ManagementMethodPhase getProjectManagementMethodologyPhaseOfProject GET Project/:projectId/MethodologyPhase Response body changed
getProjectManagementMethodTimeline GET Project/:projectId/ManagementMethodTimeline getProjectManagementMethodologyTimelineOfProject GET Project/:projectId/MethodologyTimeline Response body changed
getProjectPortfolio GET ProjectPortfolio/:portfolioId getProjectPortfolio GET ProjectPortfolio/:projectPortfolioId The parent portfolio is read with getProjectPortfolioParentPortfolio
getProjectPortfolioDependencies GET ProjectPortfolio/:portfolioId/Dependencies getAllProjectDependenciesOfProjectPortfolio GET ProjectPortfolio/:projectPortfolioId/Dependencies Renamed, returns references
getProjectPortfolioProjects GET ProjectPortfolio/:portfolioId/Projects getProjectsOfProjectPortfolio GET ProjectPortfolio/:projectPortfolioId/Projects Returns references to the projects of the portfolio; read them with getProjects
getProjectPortfolioReport GET ProjectPortfolioReport/:reportType/:reportId getProjectPortfolioReport GET ProjectPortfolioReport/:reportType/:projectPortfolioReportId Reports the validity as periodStart and periodEnd
getProjectPortfolioReports GET ProjectPortfolio/:projectPortfolioId/Reports getProjectPortfolioReportsOfProjectPortfolio GET ProjectPortfolio/:projectPortfolioId/Reports Returns references; reporttype becomes reportTypeFilter. Read the reports with getProjectPortfolioReports
getProjectPortfolioReportsByType GET ProjectPortfolio/:projectPortfolioId/Reports/:reportType getProjectPortfolioReportsOfProjectPortfolio GET ProjectPortfolio/:projectPortfolioId/Reports Returns references; the type leaves the path and becomes reportTypeFilter. Read the reports with getProjectPortfolioReports
getProjectPortfolioReportType GET ProjectPortfolioReportType/:reportTypeId getProjectPortfolioReportType GET ProjectPortfolioReportType/:projectPortfolioReportTypeId Response body changed
getProjectPortfolioReportTypes GET ProjectPortfolioReportTypes getAllProjectPortfolioReportTypes GET ProjectPortfolioReportTypes Returns references; read them with getProjectPortfolioReportTypes
getProjectPortfolioReportTypeTranslated GET ProjectPortfolioReportType/:reportTypeId/WithTranslations getProjectPortfolioReportTypeTranslated GET ProjectPortfolioReportType/:projectPortfolioReportTypeId/WithTranslations Response body changed
getProjectPortfolios GET ProjectPortfolios getAllProjectPortfolios GET ProjectPortfolios Returns references; read them with getProjectPortfolios
getProjectPortfolioSubPortfolios GET ProjectPortfolio/:portfolioId/SubPortfolios getSubPortfoliosOfProjectPortfolio GET ProjectPortfolio/:projectPortfolioId/SubPortfolios Returns references to the sub-portfolios; read them with getProjectPortfolios
getProjectReport GET ProjectReport/:reportType/:reportId/Js getProjectReport GET ProjectReport/:reportType/:projectReportId The JavaScript twin of getProjectReportHttp (Pqf.pm.getProjectReport), not redirected: with a JavaScript data collector on the report type, V2 refuses recalculate=1 with 400; the successor is the same
getProjectReportHttp GET ProjectReport/:reportType/:reportId getProjectReport GET ProjectReport/:reportType/:projectReportId recalculate is gone: the V3 read never recalculates, and recalculate=1 becomes a separate call to getProjectReportRecalculated (GET ProjectReport/:reportType/:projectReportId/Recalculated), where currency is spelled currencyId
getProjectReports GET Project/:projectId/Reports getProjectReportsOfProject GET Project/:projectId/Reports Returns references; reporttype becomes reportTypeFilter. Read the reports with getProjectReports
getProjectReportsByType GET Project/:projectId/Reports/:reportType getProjectReportsOfProject GET Project/:projectId/Reports Returns references; the type leaves the path and becomes reportTypeFilter. Read the reports with getProjectReports
getProjectReportType GET ProjectReportType/:reportTypeId getProjectReportType GET ProjectReportType/:projectReportTypeId jsDataSourceId is only returned by getProjectReportTypeTranslated
getProjectReportTypes GET ProjectReportTypes getAllProjectReportTypes GET ProjectReportTypes Returns references; read them with getProjectReportTypes
getProjectReportTypeTranslated GET ProjectReportType/:reportTypeId/WithTranslations getProjectReportTypeTranslated GET ProjectReportType/:projectReportTypeId/WithTranslations Response body changed
getProjects GET Projects getAllProjects GET Projects Returns references; read them with getProjects
getProjectScenarioMacroAllocations GET ProjectScenario/:scenarioId/MacroAllocations alc.getAllResourceAllocationsOfProjectScenario GET ALC/ProjectScenario/:projectScenarioId/ResourceAllocations Reports references with the work item as parent and takes a sort; read the objects with alc.getResourceAllocations; version is gone
getProjectsWithActiveScenario GET Projects/WithActiveScenario getAllProjectsWithActiveScenario GET ProjectsWithActiveScenario Returns references rather than whole projects; read them with getProjects. singleinactive is spelled allowSingleInactive, and a sort parameter was added
getProjectsWithMissingReport GET Projects/WithMissingReport getAllProjectsWithMissingReport GET ProjectsWithMissingReport Returns references rather than whole projects; read them with getProjects. projectstate, reportstate and reporttype are spelled projectStateIds, reportStateIds and reportTypeIds, and a sort parameter was added
getProjectScenario GET ProjectScenario/:scenarioId getProjectScenario GET ProjectScenario/:projectScenarioId Historic versions are read with getProjectScenarioTimeline and getProjectScenarioVersion, the lifecycle state with lcy.getObjectState; the owner is not available
getProjectScenarioDependencies GET ProjectScenario/:scenarioId/Dependencies getAllProjectDependenciesOfProjectScenario GET ProjectScenario/:projectScenarioId/Dependencies Renamed, returns references, no historic version
getProjectScenarioManagementMethodTimeline GET ProjectScenario/:scenarioId/ManagementMethodTimeline getProjectManagementMethodologyTimelineOfProjectScenario GET ProjectScenario/:projectScenarioId/MethodologyTimeline Response body changed
getProjectScenarioProgressCorrections GET ProjectScenario/:scenarioId/ProgressCorrections getProjectScenarioProgressCorrections GET ProjectScenario/:projectScenarioId/ProgressCorrections Response body changed: ref references the work item (type ProjectWorkItem), a day's element names its day in day and the corrections it overrode in overridden, modifiedBy is a type-and-ID reference
getProjectWorkItemProgressCorrection GET ProjectWorkItem/:workItemId/ProgressCorrection/At getProjectWorkItemProgressCorrection GET ProjectWorkItem/:projectWorkItemId/ProgressCorrection/At/:day The day is a mandatory path segment instead of the optional at; answers the correction of that day itself (pm.ProgressCorrection), or 404 if there is none, instead of a history filtered to that day
getProjectWorkItemProgressCorrections GET ProjectWorkItem/:workItemId/ProgressCorrections getProjectWorkItemProgressCorrections GET ProjectWorkItem/:projectWorkItemId/ProgressCorrections Response body changed the same way as getProjectScenarioProgressCorrections
getProjectPortfolioMilestone GET ProjectPortfolio/:portfolioId/Milestone/:milestoneId getProjectPortfolioMilestone GET ProjectPortfolioMilestone/:projectPortfolioMilestoneId Parameter list changed, response body no longer carries the validity
getProjectPortfolioMilestones GET ProjectPortfolio/:portfolioId/Milestones getProjectPortfolioMilestonesOfProjectPortfolio GET ProjectPortfolio/:projectPortfolioId/Milestones Returns references; the V3 name getProjectPortfolioMilestones is the bulk getter
getWorkItemMacroAllocations GET ProjectWorkItem/:workItemId/MacroAllocations alc.getAllResourceAllocationsOfProjectWorkItem GET ALC/ProjectWorkItem/:projectWorkItemId/ResourceAllocations Reports references with the work item as parent and takes a sort; read the objects with alc.getResourceAllocations; version is gone
proposeWorkItemEnd GET ProjectWorkItem/:workItemId/End/Proposed proposeProjectWorkItemEnd GET ProjectWorkItem/:projectWorkItemId/End/Proposed Names the milestone milestoneId
proposeWorkItemStart GET ProjectWorkItem/:workItemId/Start/Proposed proposeProjectWorkItemStart GET ProjectWorkItem/:projectWorkItemId/Start/Proposed Names the milestone milestoneId
putIndicatorIntlDetailDef PUT Indicators/:projectReportTypeId/Detail/IntlDefinition/:detailId putIndicatorDetailTranslated PUT IndicatorDetail/:indicatorDetailId/WithTranslations Addressed without the report type. Request renames dimension to indicatorDimensionId and aggregationRule to source; version and the audit members are server-managed and ignored on write; still creates the definition when the ID is unknown
putIndicatorIntlDimensionDef PUT Indicators/:projectReportTypeId/Dimension/IntlDefinition/:dimensionId putIndicatorDimensionTranslated PUT IndicatorDimension/:indicatorDimensionId/WithTranslations Addressed without the report type; version and the audit members are added to the body but are server-managed and ignored on write
putIndicatorSelections PUT Project/:projectId/IndicatorSelections/:projectReportTypeId putProjectIndicatorDetail PUT Project/:projectId/IndicatorDetail/:projectIndicatorDetailId One object per call instead of every dimension and detail of the project in one nested payload; putProjectIndicatorDimension writes a dimension
putMacroAllocation PUT MacroAllocation/:macroAllocationId alc.putResourceAllocation PUT ALC/ResourceAllocation/:resourceAllocationId Request body changed; it only updates an existing allocation, whose work item and resource cannot be changed
putProjectClassification PUT Project/:projectId/Classification putProjectClassification PUT Project/:projectId/Classification Body names the member description of the classification data remark
putProjectPortfolioMilestone PUT ProjectPortfolio/:portfolioId/Milestone/:milestoneId putProjectPortfolioMilestone PUT ProjectPortfolioMilestone/:projectPortfolioMilestoneId Parameter list changed, request body no longer carries the validity
getProjectScenarios GET Project/:projectId/Scenarios getScenariosOfProject GET Project/:projectId/Scenarios Returns references; read them with getProjectScenarios
getProjectSubObj GET Project/:projectId/SubObject/:subObjectType getProjectSubObject GET Project/:projectId/SubObject/:subObjectType Historic versions are read with getProjectSubObjectTimeline and getProjectSubObjectVersion
getProjectSubObjs GET Project/:projectId/SubObjects getProjectSubObjects GET Project/:projectId/SubObjects Response body changed
getProjectSubObjTimeline GET Project/:projectId/SubObject/:subObjectType/Timeline getProjectSubObjectTimeline GET Project/:projectId/SubObject/:subObjectType/Timeline Parameter list changed
getProjectSubObjTypes GET SubObjectTypes getAllProjectSubObjectTypes GET SubObjectTypes Returns references; read them with getProjectSubObjectTypes
getProjectSummaries GET Projects/Summaries per category, see getProjectSummary — Several projects at once: the project, active scenario, accumulated values, costs, management method, latest reports and lifecycle history successors take project IDs in bulk; relations, confidentiality, third-party references and sub objects are read per project (getProjectsSubObject reads one sub object type for several), the main phase per scenario
getProjectSummary GET Project/:projectId/Summary getProjects, getProjectSubObjects, getActiveScenarioOfProjects, getProjectWorkItemsOfProjectScenario, getAccumulated{Structure,Progress,Times,Costs}OfProjects, fco.getProjectsCostsOverviews, pf.getAllRelations, getProjectManagementMethodologyOfProjects, getProjectManagementMethodologyPhaseOfProjects, tdp.getExternalObjectAssignments, getLatestProjectReportOfProjects, acm.getConfidentiality, lcy.getObjectsHistories — No combined answer: each category of the summary is read on its own. Relations: includeImplicit=false. Latest reports: one call per report type with reportTypeFilter (and stateFilter). Main phase: the single top-most work item. Accumulated values: the project's top-most work items merged, which is the main phase's values when there is one. Costs: the overview's planned, actual, expected pending, remaining, forecast and budget totals; the currency parameter is targetCurrencyId
getProjectTimeline GET Project/:projectId/Timeline getProjectTimeline GET Project/:projectId/Timeline Takes from and until instead of beg and end
getRecentlyDeletedProjects GET Projects/RecentlyDeleted getRecentlyDeletedProjects GET Projects/Deleted/Recently Reports deletedAt and deletedBy
getRecentlyDeletedProjectScenarios GET ProjectScenarios/RecentlyDeleted getRecentlyDeletedProjectScenarios GET ProjectScenarios/Deleted/Recently Reports deletedAt and deletedBy and the project as parent
getRootProjectPortfolios GET RootProjectPortfolios getRootProjectPortfolios GET RootProjectPortfolios Returns references to the root portfolios; read them with getProjectPortfolios
getScenarioAccumulatedValues GET ProjectScenario/:scenarioId/AccumulatedValues getAccumulatedStructureOfProjectWorkItems, getAccumulatedProgressOfProjectWorkItems, getAccumulatedTimesOfProjectWorkItems, getAccumulatedCostsOfProjectWorkItems GET ProjectWorkItems/:projectWorkItemIds/AccumulatedValues/{Structure,Progress,Times,Costs} Split into four groups, each answering only the work items asked for (list them with getProjectWorkItemsOfProjectScenario). For the scenario as a whole, getAccumulated…OfProjectScenario answers one object. The currency parameter is targetCurrencyId (Costs only)
getScenarioWorkItems GET ProjectScenario/:scenarioId/WorkItems getProjectWorkItemsOfProjectScenario GET ProjectScenario/:projectScenarioId/WorkItems 2nd parameter changed
getWorkItem GET ProjectWorkItem/:workItemId getProjectWorkItems GET ProjectWorkItems/:projectWorkItemIds Parameter list changed
getWorkItemAccumulatedValues GET ProjectWorkItem/:workItemId/AccumulatedValues getAccumulatedStructureOfProjectWorkItem, getAccumulatedProgressOfProjectWorkItem, getAccumulatedTimesOfProjectWorkItem, getAccumulatedCostsOfProjectWorkItem GET ProjectWorkItem/:projectWorkItemId/AccumulatedValues/{Structure,Progress,Times,Costs} Split into four groups; the currency parameter is targetCurrencyId (Costs only)
getWorkItemLog GET ProjectWorkItem/:workItemId/Log/:logId getProjectWorkItemLogs GET ProjectWorkItemLogs/:projectWorkItemLogIds Addressed by log ID alone, reads several at once, response is pm.ProjectWorkItemLog
getWorkItemLogParent GET ProjectWorkItemLog/:logId/Parent getProjectWorkItemLogs GET ProjectWorkItemLogs/:projectWorkItemLogIds Obsolete: a log reports its work item in projectWorkItemId
getWorkItemLogs GET ProjectWorkItem/:workItemId/Logs getAllProjectWorkItemLogsOfProjectWorkItem GET ProjectWorkItem/:projectWorkItemId/Logs Returns pf.ReferenceWithParent[]; read the logs themselves with getProjectWorkItemLogs
getWorkItemProject GET ProjectWorkItem/:workItemId/Project getProjectWorkItems GET ProjectWorkItems/:projectWorkItemIds 1st parameter changed
getWorkItemShiftPlan GET ProjectWorkItem/:workItemId/ShiftPlan getProjectWorkItemShiftPlan GET ProjectWorkItem/:projectWorkItemId/ShiftPlan Response body changed: a pattern names its shift by ID in shiftId rather than embedding a whole res.ResourceShift; read the shifts with res.getResourceShifts
getWorkItemSubWorkItems GET ProjectWorkItem/:workItemId/Children getSubWorkItemsOfProjectWorkItem GET ProjectWorkItem/:projectWorkItemId/SubWorkItems Parameter list changed
putPmMethodPhaseTranslated PUT Method/:methodId/Phase/:phaseId/WithTranslations putProjectManagementMethodologyPhaseTranslated PUT Methodology/:methodId/Phase/:phaseId/WithTranslations Request body changed
putPmMethodTranslated PUT Method/:methodId/WithTranslations putProjectManagementMethodologyTranslated PUT Methodology/:methodId/WithTranslations Request body changed
putPortfolioScenario PUT PortfolioScenario/:pfScenarioId putPortfolioSimulation PUT PortfolioSimulation/:portfolioSimulationId Request body changed; validityStart / validityEnd are gone and are preserved as they were
putProject PUT Project/:projectId putProject PUT Project/:projectId Only updates an existing project; create one with addProjectPortfolioProject
putProjectConstraint PUT ProjectScenario/:scenarioId/Constraint/:constraintId putProjectMilestoneConstraint PUT ProjectMilestoneConstraint/:projectMilestoneConstraintId Updates only - create with addProjectScenarioMilestoneConstraint; movetarget / movesource became fixProjectMilestoneConstraint
putProjectDependency PUT Dependency/:dependencyId putProjectDependency PUT ProjectDependency/:projectDependencyId Request body changed
putProjectPortfolio PUT ProjectPortfolio/:portfolioId putProjectPortfolio PUT ProjectPortfolio/:projectPortfolioId Only updates an existing portfolio; create one with addProjectPortfolio or addProjectPortfolioSubPortfolio
putProjectPortfolioReport PUT ProjectPortfolioReport/:reportType/:reportId putProjectPortfolioReport PUT ProjectPortfolioReport/:reportType/:projectPortfolioReportId Only updates an existing report and takes the validity as periodStart and periodEnd; create one with addProjectPortfolioReport
putProjectPortfolioReportTypeTranslated PUT ProjectPortfolioReportType/:reportTypeId/WithTranslations putProjectPortfolioReportTypeTranslated PUT ProjectPortfolioReportType/:projectPortfolioReportTypeId/WithTranslations Request body changed
putProjectReport PUT ProjectReport/:reportType/:reportId/Js putProjectReport PUT ProjectReport/:reportType/:projectReportId The JavaScript twin of putProjectReportHttp (Pqf.pm.putProjectReport), not redirected: with a JavaScript data collector on the report type, V2 answers 202 before the collector has run; the successor is the same
putProjectReportHttp PUT ProjectReport/:reportType/:reportId putProjectReport PUT ProjectReport/:reportType/:projectReportId Only updates an existing report, takes the currency and the period in its body and leaves the indicators to putProjectReportIndicatorDimension and putProjectReportIndicatorDetail; create one with addProjectReport
putProjectReportTypeTranslated PUT ProjectReportType/:reportTypeId/WithTranslations putProjectReportTypeTranslated PUT ProjectReportType/:projectReportTypeId/WithTranslations Request body changed
putProjectScenario PUT ProjectScenario/:scenarioId putProjectScenario PUT ProjectScenario/:projectScenarioId Only updates an existing scenario and keeps its owner, which cannot be set; create one with addProjectScenario
putProjectSubObj PUT Project/:projectId/SubObject/:subObjectType putProjectSubObject PUT Project/:projectId/SubObject/:subObjectType Request body changed
putWorkItem PUT ProjectWorkItem/:workItemId putProjectWorkItem PUT ProjectWorkItem/:projectWorkItemId Parameter list changed
putWorkItemLog PUT ProjectWorkItem/:workItemId/Log/:logId putProjectWorkItemLog PUT ProjectWorkItemLog/:projectWorkItemLogId Work item moves into the body as projectWorkItemId; addProjectWorkItemLog creates one with a server-side ID
putWorkItemShiftPlan PUT ProjectWorkItem/:workItemId/ShiftPlan putProjectWorkItemShiftPlan PUT ProjectWorkItem/:projectWorkItemId/ShiftPlan Request body changed the same way; all patterns must be of the same length, and an empty shifts is refused with 400 - the delete is what drops a plan
splitWorkItem POST ProjectWorkItem/:workItemId/Split splitProjectWorkItem POST ProjectWorkItem/:projectWorkItemId/Split 2nd parameter changed
updateProjectWorkItemProgressCorrection POST ProjectWorkItem/:workItemId/ProgressCorrection/Update putProjectWorkItemProgressCorrection PUT ProjectWorkItem/:projectWorkItemId/ProgressCorrection/At/:day A PUT whose day is a mandatory path segment; remainingEffort or progress and remark move into the body (pm.ProgressCorrectionChange), includestimebased / deleteoverride are spelled includesTimeBased / deleteOverride

Retired — no V3 successor; the endpoint disappears with API V2.

V2 endpoint V2 path Note
getProjectDetails GET Project/:projectId/Details Use getProject() / GET API/V2/PM/Project/:id instead, will be discontinued at end of 2028
getProjectScenarioChangesTimeline GET ProjectScenario/:scenarioId/HistoryTimeline Will be replaced by an API V3 endpoint that also covers the changes of the Allocation 2.0 feature as soon as that feature is released. API V2 will be discontinued at end of 2028
putProjectDetails PUT Project/:projectId/Details Use putProject() / PUT API/V2/PM/Project/:id instead, will be discontinued at end of 2028

getProjectScenarioChangesTimeline is the one retirement that is only deferred: keep using it on V2 until its V3 successor is released; this guide will then name it.

New in V3 — no V2 equivalent.

addProjectWorkItemLog, delIndicatorClassificationTranslated, delIndicatorClassificationValueTranslated, delProjectIndicatorDetail, delProjectIndicatorDimension, delProjectReportIndicatorDetail, delProjectReportIndicatorDimension, delProjectSubObjectTypeTranslated, getActiveScenarioOfProjects, getAllIndicatorClassificationValues, getAllIndicatorClassificationValuesByClassification, getAllIndicatorDetails, getAllIndicatorDimensionDetails, getAllIndicatorDimensions, getAllProjectDependenciesOfProject, getAllProjectDependenciesOfProjectPortfolio, getAllProjectDependenciesOfProjectScenario, getAllProjectPortfolioMilestones, getAllProjectScenarios, getAllProjectWorkItemLogsOfProjectScenario, getIndicatorClassificationValueTranslated, getIndicatorClassificationValues, getIndicatorClassificationValuesTranslated, getIndicatorClassificationsTranslated, getIndicatorDetails, getIndicatorDetailsTranslated, getIndicatorDimensions, getIndicatorDimensionsTranslated, getPortfolioSimulationIdentification, getPortfolioSimulationIdentifications, getPortfolioSimulations, getProjectIndicatorDetailIdentification, getProjectIndicatorDetailIdentifications, getProjectIndicatorDetails, getProjectIndicatorDimension, getProjectIndicatorDimensionIdentification, getProjectIndicatorDimensionIdentifications, getProjectIndicatorDimensionTimeline, getProjectIndicatorDimensionVersion, getProjectIndicatorDimensions, getProjectManagementMethodologies, getProjectManagementMethodologiesTranslated, getProjectManagementMethodologyOfProjects, getProjectManagementMethodologyPhaseOfProjects, getProjectManagementMethodologyPhases, getProjectManagementMethodologyPhasesTranslated, getProjectManagementMethodologyTimelineOfProjectScenarios, getProjectManagementMethodologyTimelineOfProjects, getProjectMilestone, getProjectMilestones, getProjectPortfolioIdentification, getProjectPortfolioIdentifications, getProjectPortfolioMilestoneIdentification, getProjectPortfolioMilestoneIdentifications, getProjectPortfolioMilestonesOfProjectPortfolio, getProjectPortfolioParentPortfolio, getProjectPortfolioReportIdentification, getProjectPortfolioReportIdentifications, getProjectPortfolioReportTypesTranslated, getProjectPortfolioReportsOfType, getProjectPortfolioTimeline, getProjectPortfolioVersion, getProjectReportIdentification, getProjectReportIdentifications, getProjectReportIndicatorDetail, getProjectReportIndicatorDetailIdentification, getProjectReportIndicatorDetailIdentifications, getProjectReportIndicatorDetails, getProjectReportIndicatorDimension, getProjectReportIndicatorDimensionIdentification, getProjectReportIndicatorDimensionIdentifications, getProjectReportIndicatorDimensions, getProjectReportRecalculated, getProjectReportTypesTranslated, getProjectReportsOfType, getProjectScenarioIdentification, getProjectScenarioIdentifications, getProjectScenarioTimeline, getProjectScenarioVersion, getProjectSubObjectType, getProjectSubObjectTypeTranslated, getProjectSubObjectTypesTranslated, getProjectSubObjectVersion, getProjectVersion, getProjectWorkItem, getProjectWorkItemIdentification, getProjectWorkItemIdentifications, getProjectWorkItemLog, getProjectWorkItemLogIdentification, getProjectWorkItemLogIdentifications, getProjectWorkItemLogTimeline, getProjectWorkItemLogVersion, getProjectWorkItemTimeline, getProjectWorkItemVersion, getProjectsSubObject, getScenariosOfProjects, putIndicatorClassificationTranslated, putIndicatorClassificationValueTranslated, putProjectMilestone, putProjectReportIndicatorDetail, putProjectReportIndicatorDimension, putProjectSubObjectTypeTranslated

  • Holidays are addressed globally. In V2 a holiday lived under its locale (HolidayLocale/:locid/Holiday/:holid); in V3 it has its own ID (Holiday/:holidayId), and several at once are Holidays/:holidayIds. The separate …Bulk endpoints disappear — for reading and deleting, the plural endpoint is the bulk endpoint.
  • The bulk write is the exception, and it is worth using. putHolidaysBulk becomes updateHolidays (POST HolidayLocale/:holidayLocaleId/Holidays/Update), the one holiday endpoint that keeps the locale in the URL: a holiday that does not exist yet has no locale to look up. Each element of the request body carries its own id and localeId, so the parallel ID array of the V2 URL is gone. Prefer it over a putHoliday per holiday — a single write of a holiday recalculates the availability, presence and work plan of every resource that works by its locale, and one updateHolidays request pays that price once instead of once per holiday.
  • Availabilities collapse into one endpoint. getAvailabilities and getAvailabilitiesByResource both become getResourceAvailabilities.
  • The holiday-locale "details" and "obj" endpoints are retired: use getHolidayLocale / putHolidayLocale.
  • Resources, resource links and holiday locales are V3 object families now, with identifications and reference lists (getAllResourceTypes, getAllResourceGroups, getAllResourceLinkTypes, getResourceLinks).
  • The hierarchy endpoints are gone; the hierarchy is assembled from parts. getResourceHierarchy and getResourceHierarchyByType returned a ready-made tree. In V3 you read the resources (getAllResourcesByTypes / getResourceIdentifications) and their parent links (getResourcesParentsLinks) and build the tree yourself — which is also what makes the per-link-type variants (getResourceParentsByLinkType, getResourcesChildrenLinks, …) possible. Note that a resource's own validity dates are not part of the V3 answer.
  • addResourceChild is the one add… endpoint whose URL is not …/Add — it stays Resource/:parentResourceId/AddChild — and it now takes linkTypeId plus the basic field initializers.
  • New: work-time units and factors, standard work time per group, children by link type, and the holiday list of a locale.
  • The availability of the V2 allocation slots is a RES time slot. getResourceAvailabilitySlots answers the working time without absences in time slots, before the base load of the worktime model is taken off — V2's alc.getMacroAllocationSlots reported it after. It has a bulk sibling and a single-slot lookup; the family is described in the ALC section.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
addHolidayLocale POST HolidayLocales/Add addHolidayLocale POST HolidayLocales/Add
addResourceChild POST Resource/:parentResourceId/AddChild addResourceChild POST Resource/:parentResourceId/AddChild
addResourceLink POST Resource/:fromId/AddLink addResourceLink POST Resource/:fromId/AddLink
delAvailability DELETE Resource/:resourceId/Availability/:availabilityId delAvailability DELETE Resource/:resourceId/Availability/:availabilityId
delHolidayLocale DELETE HolidayLocale/:locid delHolidayLocale DELETE HolidayLocale/:holidayLocaleId
delResourceLink DELETE ResourceLink/:linkType/:linkId delResourceLink DELETE ResourceLink/:linkType/:resourceLinkId
getResourceKind GET ResourceKind/:kindId getResourceKind GET ResourceKind/:kind
getResourceKinds GET ResourceKinds getResourceKinds GET ResourceKinds
getWeekDaysToWorkDaysMapping GET Resource/:resourceId/WeekDaysToWorkDaysMapping getWeekDaysToWorkDaysMapping GET Resource/:resourceId/WeekDaysToWorkDaysMapping
getWorkDaysToWeekDaysMapping GET Resource/:resourceId/WorkDaysToWeekDaysMapping getWorkDaysToWeekDaysMapping GET Resource/:resourceId/WorkDaysToWeekDaysMapping
importHolidays POST HolidayLocale/:locid/Holidays/Import importHolidays POST HolidayLocale/:holidayLocaleId/Holidays/Import

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
delHoliday DELETE HolidayLocale/:locid/Holiday/:holid delHoliday DELETE Holiday/:holidayId Addresses the holiday by its ID alone
delHolidaysBulk DELETE HolidayLocale/:locid/Holidays/:holids delHolidays DELETE Holidays/:holidayIds Parameter list changed
delResource DELETE Resource/:resourceId delResource DELETE Resource/:resourceId Recursive deletion is not available, hard-linked children have to be moved or deleted first
getAvailabilities GET Resource/:resourceId/Availabilities getResourceAvailabilities GET Resource/:resourceId/Availabilities Parameter list changed
getAvailabilitiesByResource GET Availabilities/ByResource/:resourceIds getResourceAvailabilities GET Resource/:resourceId/Availabilities Parameter list changed
getAvailability GET Resource/:resourceId/Availability/:availabilityId getAvailability GET Resource/:resourceId/Availability/:availabilityId Response body changed
getHoliday GET HolidayLocale/:locid/Holiday/:holid getHoliday GET Holiday/:holidayId Addresses the holiday by its ID alone
getHolidayLocale GET HolidayLocale/:locid getHolidayLocale GET HolidayLocale/:holidayLocaleId Requires READ permission; getHolidayLocaleIdentification requires IDENTIFY only
getHolidayLocales GET HolidayLocales getAllHolidayLocales GET HolidayLocales Returns references; read the locales with getHolidayLocales
getHolidays GET HolidayLocale/:locid/Holidays getHolidayLocaleHolidays GET HolidayLocale/:holidayLocaleId/Holidays Returns references; read them with getHolidays
getHolidaysBulk GET HolidayLocale/:locid/Holidays/:holids getHolidays GET Holidays/:holidayIds Parameter list changed
getRecentlyDeletedResources GET Resources/RecentlyDeleted getRecentlyDeletedResources GET Resources/Deleted/Recently Requires the administration permission and returns identifications with deletedAt and deletedBy; the resource administration permission alone is not sufficient
getResource GET Resource/:resourceId getResource GET Resource/:resourceId Historic versions are read with getResourceTimeline and getResourceVersion instead of version
getResourceAncestors GET Resource/:resourceId/Ancestors getResourceAncestors GET Resource/:resourceId/Ancestors Returns references to the ancestors; read them with getResources
getResourceChildren GET Resource/:resourceId/Children getResourceChildren GET Resource/:resourceId/Children Returns references to the children; read them with getResources
getResourceChildrenIdentification GET Resource/:resourceId/ChildrenIdentification getResourceChildren GET Resource/:resourceId/Children Parameter list changed
getResourceGroup GET ResourceGroup/:groupId getResourceGroup GET ResourceGroup/:groupId Response body changed
getResourceGroups GET ResourceGroups getAllResourceGroups GET ResourceGroups Returns references; read them with getResourceGroups
getResourceGroupStandardWorktimes GET ResourceGroups/StandardWorktimes getResourceGroupsStandardWorktime GET ResourceGroups/:groupIds/StandardWorktime Parameter list changed
getResourceGroupWorktimeUnitFactors GET ResourceGroup/:groupId/WorktimeUnitFactors getResourceGroupWorktimeUnitFactors GET ResourceGroup/:groupId/WorktimeUnitFactors Names the unit unitId
getResourceGroupWorktimeUnits GET ResourceGroup/:groupId/WorktimeUnits getResourceGroupWorktimeUnits GET ResourceGroup/:groupId/WorktimeUnits Names the member abbr of a unit abbreviation
getResourceIdentification GET Resource/:resourceId/Identification getResourceIdentification GET Resource/:resourceId/Identification The links to the parents are read with getResourceParentsLinks
getResourceLink GET ResourceLink/:linkType/:linkId getResourceLink GET ResourceLink/:linkType/:resourceLinkId Returns the V3 ResourceLink including the linked resources
getResourceLinkType GET ResourceLinkType/:linkTypeId getResourceLinkType GET ResourceLinkType/:linkTypeId Names the implicit relation type implicitRelationTypeId
getResourceLinkTypes GET ResourceLinkTypes getAllResourceLinkTypes GET ResourceLinkTypes Returns references; read them with getResourceLinkTypes
getResources GET Resources getAllResourcesByTypes GET ResourceTypes/:= length > 0;non-null">typeIds/Resources Returns references per resource type (list the types with getAllResourceTypes); read the resources with getResources
getResourcesByGroup GET ResourceGroup/:groupId/Resources getAllResourcesByGroup GET ResourceGroup/:groupId/Resources Returns references to the resources; read them with getResourceIdentifications
getResourcesByKind GET ResourceKind/:kindId/Resources getAllResourcesByKind GET ResourceKind/:kind/Resources Returns references to the resources; read them with getResourceIdentifications
getResourcesByKinds GET ResourceKinds/:kindIds/Resources getAllResourcesByKind GET ResourceKind/:kind/Resources Parameter list changed
getResourcesByType GET ResourceType/:typeId/Resources getAllResourcesByType GET ResourceType/:typeId/Resources Returns references to the resources; read them with getResources; reading them requires READ permission
getResourcesByTypes GET ResourceTypes/:typeIds/Resources getAllResourcesByTypes GET ResourceTypes/:typeIds/Resources Returns references to the resources; read them with getResources; reading them requires READ permission
getResourceShift GET ResourceShift/:shiftid getResourceShift GET ResourceShift/:shiftId Response body changed
getResourceShifts GET ResourceShifts getResourceShifts GET ResourceShifts Response body changed
getResourceHierarchy GET Resources/ResourceHierarchy getAllResourceTypes + getAllResourcesByTypes + getResourceIdentifications + getResourcesParentsLinks see section prose The ready-made tree is gone; assemble it from resources and parent links. The resource's own validity dates are not available in V3
getResourceHierarchyByType GET ResourceType/:typeId/ResourceHierarchy getAllResourcesByType + getResourceIdentifications + getResourcesParentsLinks see section prose The ready-made tree is gone; assemble it from resources and parent links. The resource's own validity dates are not available in V3
getResourceTimeline GET Resource/:resourceId/Timeline getResourceTimeline GET Resource/:resourceId/Timeline Returns the version timeline and takes from and until instead of beg and end; it requires READ permission instead of IDENTIFY
getResourceType GET ResourceType/:typeId getResourceType GET ResourceType/:typeId Response body changed
getResourceTypes GET ResourceTypes getAllResourceTypes GET ResourceTypes Returns references; read them with getResourceTypes
previewHolidays POST Holidays/Import/Preview previewHolidays POST Holidays/Import/Preview The format value ical becomes Ical
putAvailability PUT Resource/:resourceId/Availability/:availabilityId putAvailability PUT Resource/:resourceId/Availability/:availabilityId Request body changed
putHoliday PUT HolidayLocale/:locid/Holiday/:holid putHoliday PUT Holiday/:holidayId Addresses the holiday by its ID and takes the V3 Holiday, including its localeId, as its body
putHolidaysBulk PUT HolidayLocale/:locid/Holidays/:holids updateHolidays POST HolidayLocale/:holidayLocaleId/Holidays/Update Request body changed
putHolidayLocale PUT HolidayLocale/:locid putHolidayLocale PUT HolidayLocale/:holidayLocaleId Takes the V3 HolidayLocale as its body and only updates an existing locale, which requires the multiholidaylocale license; create one with addHolidayLocale
putResource PUT Resource/:resourceId putResource PUT Resource/:resourceId Takes the V3 Resource as its body and only updates an existing resource; create one under a parent with addResourceChild
putResourceGroupStandardWorktime PUT ResourceGroup/:resgrpid/StandardWorktime putResourceGroupStandardWorktime PUT ResourceGroup/:groupId/StandardWorktime Body names the resource group resourceGroupId
putResourceLink PUT ResourceLink/:linkType/:linkId putResourceLink PUT ResourceLink/:linkType/:resourceLinkId Takes the V3 ResourceLink as its body

Retired — no V3 successor; the endpoint disappears with API V2.

V2 endpoint V2 path Note
getHolidayLocaleDetails GET HolidayLocale/:locid/Details Use getHolidayLocale(...) instead, will be discontinued at end of 2028
getHolidayLocaleObj JS only Use getHolidayLocale(...) instead, will be discontinued at end of 2028
putHolidayLocaleDetails PUT HolidayLocale/:locid/Details Use putHolidayLocale(...) instead, will be discontinued at end of 2028
putHolidayLocaleObj JS only Use getHolidayLocale(...) instead, will be discontinued at end of 2028

New in V3 — no V2 equivalent.

getAvailabilities, getHolidayLocaleIdentification, getHolidayLocaleIdentifications, getResourceChildrenByLinkType, getResourceChildrenLinks, getResourceChildrenLinksByLinkType, getResourceGroupStandardWorktime, getResourceGroupsWorktimeUnitFactors, getResourceGroupsWorktimeUnits, getResourceIdentifications, getResourceLinkIdentification, getResourceLinkIdentifications, getResourceLinks, getResourceParents, getResourceParentsByLinkType, getResourceParentsLinks, getResourceParentsLinksByLinkType, getResourceVersion, getResourcesAvailabilitySlot, getResourcesChildren, getResourcesChildrenByLinkType, getResourcesChildrenLinks, getResourcesChildrenLinksByLinkType, getResourcesParents, getResourcesParentsByLinkType, getResourcesParentsLinksByLinkType

RSK — Risks

Risks are a V3 object family: identifications, version timeline, historic versions. getAllProjectRisks and getProjectRisksOfProjectPortfolio let you read risks across projects instead of one project at a time. Most endpoints are reimplemented with new bodies, so plan on rewriting rather than re-pointing.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
addProjectRisk POST Project/:prjid/Risks/Add addProjectRisk POST Project/:projectId/Risks/Add

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
calculateRisk POST RiskMatrix/Calculate calculateRisk POST RiskMatrix/Calculate Names the classifications …ClassificationId
delProjectRisk DELETE Project/:prjid/Risk/:riskId delProjectRisk DELETE ProjectRisk/:projectRiskId Addresses the risk by its ID alone
getAllProjectRisks GET ProjectRisks getAllProjectRisks GET ProjectRisks Returns references; read them with getProjectRisks; currency is gone
getProjectPortfolioRisks GET ProjectPortfolio/:ppfid/Risks getProjectRisksOfProjectPortfolio GET ProjectPortfolio/:projectPortfolioId/Risks Parameter list changed
getProjectPortfolioRisksWithHistory GET ProjectPortfolio/:ppfid/Risks/WithHistory/:history getProjectRisksOfProjectPortfolio GET ProjectPortfolio/:projectPortfolioId/Risks Parameter list changed
getProjectRisk GET Project/:prjid/Risk/:riskId getProjectRisk GET ProjectRisk/:projectRiskId Addresses the risk by its ID alone; historic versions are read with getProjectRiskTimeline and getProjectRiskVersion
getProjectRiskById GET ProjectRisk/:riskId getProjectRisks GET ProjectRisks/:projectRiskIds Parameter list changed
getProjectRisks GET Project/:prjid/Risks getProjectRisksOfProject GET Project/:projectId/Risks Returns references; read them with getProjectRisks; version and currency are gone
getProjectRisksWithHistory GET Project/:prjid/Risks/WithHistory/:history getProjectRisksOfProject GET Project/:projectId/Risks Returns references; read them with getProjectRisksWithHistory; version and currency are gone, simplify stays
getProjectRiskWithHistory GET Project/:prjid/Risk/:riskId/WithHistory/:history getProjectRiskWithHistory GET ProjectRisk/:projectRiskId/WithHistory/:history Addresses the risk by its ID alone and takes simplify as a calendar granularity; the history up to a historic version and simplification by decade are not available
getRiskMatrixDefinition GET RiskMatrix/Definition getRiskMatrixDefinition GET RiskMatrix/Definition Response body changed
getRiskMatrixDefinitionTranslated GET RiskMatrix/Definition/WithTranslations getRiskMatrixDefinitionTranslated GET RiskMatrix/Definition/WithTranslations Requires the administration permission
putProjectRisk PUT Project/:prjid/Risk/:riskId putProjectRisk PUT ProjectRisk/:projectRiskId Addresses the risk by its ID alone, takes the V3 ProjectRisk as its body and only updates an existing risk; create one with addProjectRisk
putRiskMatrixDefinitionTranslated PUT RiskMatrix/Definition/WithTranslations putRiskMatrixDefinitionTranslated PUT RiskMatrix/Definition/WithTranslations Takes the V3 RiskMatrixDefinitionTranslated as its body

New in V3 — no V2 equivalent.

getProjectRiskIdentification, getProjectRiskIdentifications, getProjectRiskTimeline, getProjectRiskVersion

STG — Strategy

Fully migrated, mostly reimplemented — every put… body changed, so budget for a rewrite of all write paths.

Renames to know:

V2 V3
getStrategies getAllStrategies
getVisions getVisionsOfStrategy
getProjectContributions getProjectContributionsOfObjective
getProjectContribution getProjectContributions (bulk by ID)
get…History get…Timeline

Note that getStrategies, getVisions, getObjectives, getObjectiveIndicators and getFieldOfActions all exist in V3 as bulk getters by ID — which is not what the identically spelled V2 names meant. Check every one of them.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
addFieldOfAction POST Vision/:visionId/FieldsOfAction/Add addFieldOfAction POST Vision/:visionId/FieldsOfAction/Add
addObjective POST FieldOfAction/:foaId/Objectives/Add addObjective POST FieldOfAction/:fieldOfActionId/Objectives/Add
addObjectiveIndicator POST Objective/:objectiveId/ObjectiveIndicators/Add addObjectiveIndicator POST Objective/:objectiveId/ObjectiveIndicators/Add
addProjectContribution POST Objective/:objectiveId/ProjectContributions/Add addProjectContribution POST Objective/:objectiveId/ProjectContributions/Add
addStrategy POST Strategies/Add addStrategy POST Strategies/Add
addVision POST Strategy/:strategyId/Visions/Add addVision POST Strategy/:strategyId/Visions/Add
delFieldOfAction DELETE FieldOfAction/:foaId delFieldOfAction DELETE FieldOfAction/:fieldOfActionId
delObjective DELETE Objective/:objectiveId delObjective DELETE Objective/:objectiveId
delObjectiveIndicator DELETE ObjectiveIndicator/:indicatorId delObjectiveIndicator DELETE ObjectiveIndicator/:objectiveIndicatorId
delProjectContribution DELETE ProjectContribution/:contributionId delProjectContribution DELETE ProjectContribution/:projectContributionId
delStrategy DELETE Strategy/:strategyId delStrategy DELETE Strategy/:strategyId
delVision DELETE Vision/:visionId delVision DELETE Vision/:visionId

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getAllFieldsOfAction GET FieldsOfAction getAllFieldsOfAction GET FieldsOfAction Parameter list changed
getAllObjectiveIndicators GET ObjectiveIndicators getAllObjectiveIndicators GET ObjectiveIndicators Parameter list changed
getAllObjectives GET Objectives getAllObjectives GET Objectives Parameter list changed
getAllProjectContributions GET ProjectContributions getAllProjectContributions GET ProjectContributions Parameter list changed
getAllVisions GET Visions getAllVisions GET Visions Parameter list changed
getContributionsByProject GET Project/:projectId/Contributions getProjectContributionsOfProject GET Project/:projectId/ProjectContributions Parameter list changed
getFieldOfAction GET FieldOfAction/:foaId getFieldOfAction GET FieldOfAction/:fieldOfActionId Response body changed
getFieldOfActionHistory GET FieldOfAction/:foaId/History getFieldOfActionTimeline GET FieldOfAction/:fieldOfActionId/Timeline Parameter list changed
getFieldsOfAction GET Vision/:visionId/FieldsOfAction getFieldsOfActionOfVision GET Vision/:visionId/FieldsOfAction Parameter list changed
getObjective GET Objective/:objectiveId getObjective GET Objective/:objectiveId Response body changed
getObjectiveHistory GET Objective/:objectiveId/History getObjectiveTimeline GET Objective/:objectiveId/Timeline Parameter list changed
getObjectiveIndicator GET ObjectiveIndicator/:indicatorId getObjectiveIndicator GET ObjectiveIndicator/:objectiveIndicatorId Response body changed
getObjectiveIndicatorHistory GET ObjectiveIndicator/:indicatorId/History getObjectiveIndicatorTimeline GET ObjectiveIndicator/:objectiveIndicatorId/Timeline Parameter list changed
getObjectiveIndicators GET Objective/:objectiveId/Indicators getObjectiveIndicatorsOfObjective GET Objective/:objectiveId/ObjectiveIndicators Parameter list changed
getObjectives GET FieldOfAction/:foaId/Objectives getObjectivesOfFieldOfAction GET FieldOfAction/:fieldOfActionId/Objectives Parameter list changed
getProjectContribution GET ProjectContribution/:contributionId getProjectContributions GET ProjectContributions/:projectContributionIds 1st parameter changed
getProjectContributionHistory GET ProjectContribution/:contributionId/History getProjectContributionTimeline GET ProjectContribution/:projectContributionId/Timeline Parameter list changed
getProjectContributions GET Objective/:objectiveId/ProjectContributions getProjectContributionsOfObjective GET Objective/:objectiveId/ProjectContributions Parameter list changed
getStrategies GET Strategies getAllStrategies GET Strategies Parameter list changed
getStrategy GET Strategy/:strategyId getStrategy GET Strategy/:strategyId Response body changed
getStrategyHistory GET Strategy/:strategyId/History getStrategyTimeline GET Strategy/:strategyId/Timeline Parameter list changed
getVision GET Vision/:visionId getVision GET Vision/:visionId Response body changed
getVisionHistory GET Vision/:visionId/History getVisionTimeline GET Vision/:visionId/Timeline Parameter list changed
getVisions GET Strategy/:strategyId/Visions getVisionsOfStrategy GET Strategy/:strategyId/Visions Parameter list changed
putFieldOfAction PUT FieldOfAction/:foaId putFieldOfAction PUT FieldOfAction/:fieldOfActionId Request body changed
putObjective PUT Objective/:objectiveId putObjective PUT Objective/:objectiveId Request body changed
putObjectiveIndicator PUT ObjectiveIndicator/:indicatorId putObjectiveIndicator PUT ObjectiveIndicator/:objectiveIndicatorId Request body changed
putProjectContribution PUT ProjectContribution/:contributionId putProjectContribution PUT ProjectContribution/:projectContributionId Request body changed
putStrategy PUT Strategy/:strategyId putStrategy PUT Strategy/:strategyId Request body changed
putVision PUT Vision/:visionId putVision PUT Vision/:visionId Request body changed

New in V3 — no V2 equivalent.

getFieldOfActionIdentification, getFieldOfActionIdentifications, getFieldOfActionVersion, getFieldOfActions, getObjectiveIdentification, getObjectiveIdentifications, getObjectiveIndicatorIdentification, getObjectiveIndicatorIdentifications, getObjectiveIndicatorVersion, getObjectiveIndicators, getObjectiveVersion, getObjectives, getProjectContributionVersion, getStrategies, getStrategyIdentification, getStrategyIdentifications, getStrategyVersion, getVisionIdentification, getVisionIdentifications, getVisionVersion, getVisions

TDP — External (third-party) object references

This module was renamed wholesale. The V2 endpoints were named after four different things — Reference, ForeignItem, ForeignKeyReference, ForeignKeyAssignments — for what is really one mirrored external object plus one piece of configuration. All ten V2 endpoints are deprecated, and only the delete-by-key endpoint could be redirected — every other body contract changed. Plan a rewrite of the whole integration surface.

The new vocabulary:

Concept V3 name
A PQFORCE object type that may carry external objects, with its maximum count AssignableObjectType
An external object mirrored in PQFORCE ExternalObject
The external system's own key for it externalKey
The PQFORCE object it is assigned to assignedTo
URL parameters objectType / objectId / externalObjectId

Member renames inside the payload:

V2 V3
foreignName / foreignCode / foreignDescription name / code / description
foreignImageUrl iconUrl
foreignKey externalKey
pqforceId assignedTo — a pf.Reference, so the type is explicit
pqforceName / pqforceCode / pqforceDescription / pqforceImageUrl not in the payload; read them with the identification endpoint of the assigned object's module
typeId (on the type contract) objectType
additionalData unchanged

Two contracts disappear: the "with own data" variant (the PQFORCE object's name, code, description and image) has no successor, and the assignment contract is replaced by a plain pf.Reference array.

Behaviour worth knowing:

  • getForeignItem and getForeignItems are dropped, not renamed. They returned the same rows as the reference endpoints in a different shape, and put the external key where the ID belonged. Use getExternalObjectByKey (or getExternalObjectIdentification) and getAllExternalObjects.
  • A single assignment can now be changed atomically: assignExternalObject and unassignExternalObject take an objectId and change one assignment, instead of reading the whole set and writing it back. Prefer them over putExternalObjectAssignments whenever you change one link, so a concurrent change cannot be lost.
  • putExternalObject rejects a changed externalKey with 400, because the key is part of the object's identity.
  • assigned and unassigned on getAllExternalObjects are still two independent booleans, both defaulting to true — including the quirk that setting both to false yields an empty list.
  • getAllExternalObjects answers references, not whole objects, each one carrying the externalKey and, for an assigned object, the PQFORCE object it is assigned to as its parent. Read the objects themselves with getExternalObjects/:objectType/:externalObjectIds. It also takes a sort now, over type, id, externalKey, name, code, description and assignedTo.
  • ExternalObject.type is still 3rdPartyRef/<objectType> and its id is still derived from (objectType, externalKey), exactly as in V2. Existing IDs stay valid.
  • New in V3: the full object family — bulk read, put, del, identifications, version timeline, historic versions, recently deleted and undelete — none of which V2 offered.

Redirected — the V2 URL answers 307 with the V3 location; request and response are identical.

V2 endpoint V2 path V3 endpoint V3 path
delForeignKeyReference DELETE :type/ForeignKeyReference/:foreignkey delExternalObjectByKey DELETE ExternalObjectByKey/:objectType/:externalKey

Reimplemented — no redirect; the call has to be rewritten.

V2 endpoint V2 path V3 endpoint V3 path What differs
getForeignItem GET :type/ForeignItem/:foreignKey getExternalObjectByKey GET ExternalObjectByKey/:objectType/:externalKey Response body changed
getForeignItems GET :type/ForeignItems getAllExternalObjects GET ExternalObjects/:objectType Parameter list changed, returns references; read the objects with getExternalObjects/:ids
getForeignKeyAssignments GET :type/:id/ForeignKeyAssignments getExternalObjectAssignments GET Object/:objectType/:objectId/ExternalObjects Response body changed
getForeignKeyReference GET :type/ForeignKeyReference/:foreignkey getExternalObjectByKey GET ExternalObjectByKey/:objectType/:externalKey Response body changed
getReference GET :type/Reference/:internalId getExternalObject GET ExternalObject/:objectType/:externalObjectId Response body changed
getReferences GET :type/References getAllExternalObjects GET ExternalObjects/:objectType Returns references; read the objects with getExternalObjects/:ids
getReferenceTypes GET ReferenceTypes getAllAssignableObjectTypes GET AssignableObjectTypes Response body changed
putForeignKeyAssignments PUT :type/:id/ForeignKeyAssignments putExternalObjectAssignments PUT Object/:objectType/:objectId/ExternalObjects Request body changed
putForeignKeyReference PUT :type/ForeignKeyReference/:foreignkey putExternalObjectByKey PUT ExternalObjectByKey/:objectType/:externalKey Request body changed; an absent assignedTo keeps the assignment, where V2's absent pqforceId cleared it

New in V3 — no V2 equivalent.

assignExternalObject, delExternalObject, getAssignableObjectType, getExternalObjectIdentification, getExternalObjectIdentifications, getExternalObjectTimeline, getExternalObjectVersion, getExternalObjects, getRecentlyDeletedExternalObjects, putExternalObject, unassignExternalObject, undelExternalObject


11. A practical migration order

If you are planning the work rather than reading it endpoint by endpoint:

  1. Inventory what you actually call. Run your integration or automation once and collect every response that carries a Deprecation header (or, in a JavaScript automation, every deprecation warning in the console). That list, not this document, is your scope.
  2. Regenerate your client from the V3 OpenAPI description (section 3) into a separate namespace, so V2 and V3 calls can coexist while you move.
  3. Take the redirects first. They are URL or function-name changes with no behavioural risk, and they clear a large part of the list cheaply. Check that your HTTP client follows 307 for PUT and POST before you start.
  4. Then the reads. Convert list screens to the reference-plus-page pattern (section 5.2) and switch label-only views to identifications (section 5.5). This is where V3 pays off, and it is low-risk: reads are idempotent.
  5. Then the writes. Request bodies changed on many put… endpoints. Do these module by module, with the reference documentation open, and delete the follow-up PUT after each Add you convert (section 5.8).
  6. Handle the retirements. Section 8 and the Retired tables in section 10. These need a decision, not a rewrite — a replacement mechanism, or dropping the feature.
  7. Finish before the end of 2028. Every call you make has a V3 successor or a retirement decision, so a complete migration is possible today. Check the Deprecation headers once more at the end: when none is left, you are done.

Two things worth doing while you are in the code anyway: switch anything that polls to the change-notification WebSocket (section 5.14), and delete client-side sorting in favour of the sort parameter (section 5.4).


12. How this document is maintained

The migration from V2 to V3 is complete, so this document no longer grows release by release. It is revised only when something a migrating developer needs to know changes:

  • Sections 1–9 describe concepts and change rarely.
  • Section 10 changes when a deferred endpoint gets its V3 successor — currently only PM getProjectScenarioChangesTimeline — or when a change to an existing V3 endpoint becomes unavoidable. Such a change is announced through the usual deprecation channel — a Deprecation header and a note in the reference documentation — not silently.
  • Counts in this document — the totals in section 2, the per-module table in section 2.5 — describe the release named at the top. Compare them against the release you are running.
Release What changed in this guide
4.26.6 First edition. Covers the complete V2 → V3 migration (100 %): ACM, ACT, ALC, ATM, CHT, CLF, CLS, EXP, FCO, LCY, LIC, MAIN, MSG, MTG, PF, PI, PM, RES, RSK, STG and TDP fully migrated; DOC, DPO and NAV retired. The work-item object type became ProjectWorkItem (section 6).
↑

Benachrichtigung

Portal-Tour

Eine geführte Tour zeigt Ihnen die Seite Schritt für Schritt: Sie hebt jeweils ein Bedienelement hervor und erklärt, wofür es da ist. Sie können jederzeit abbrechen und später weitermachen.