Last date modified: 2026-Oct-07
ARM API (REST) V1 to V3 migration guide - part 2
This guide is for developers integrating with the ARM REST API. It covers migrating the following endpoints from the unversioned Kepler API, V1, or V2 to the public V3 API:
| Endpoint | Status |
|---|---|
POST /relativity-arm/v3/jobs/list
|
Available now |
GET /relativity-arm/v3/jobs/{jobId}/status
|
Available now |
GET /relativity-arm/v3/jobs/{jobId}/statistics
|
Available now |
POST /relativity-arm/v3/restore-jobs/available-archives
|
Available now |
GET /relativity-arm/v3/archive-jobs/directories
|
Available now |
This is the second part of the migration guide. For the first part covering breaking changes and other endpoints, see ARM API (REST) V1 to V3 migration guide - part 1.
List Jobs
Old approach
Clients queried the JobLocation, JobCaseInformation, Jobs, and JobExecutions tables directly via SQL. Some used the private V2 endpoint:
1
GET /relativity-arm-private/v2/jobs-list/view/{search}
New endpoint
1
POST /relativity-arm/v3/jobs/list
Request body
All V3 POST and PUT endpoints wrap their body in a top-level Request object.
1
2
3
4
5
6
7
{
"Request": {
"View": "All",
"ContinuationToken": null,
"PageSize": 100
}
}
| Field | Type | Required | Notes |
|---|---|---|---|
View
|
string (enum) | Yes | All, Transient (active/queued), Completed (finished) |
ContinuationToken
|
string | No | Omit on the first request. Pass the token from the previous response to get the next page. |
PageSize
|
integer | No | 1–1000. Defaults to 100 when omitted. |
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"Jobs": [
{
"JobID": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"JobName": "archive-3fa85f64",
"JobPriority": "Medium",
"JobState": "InProgress",
"JobType": "Archive",
"TimeElapsed": "00:00:11:38",
"WorkspaceID": 1234567,
"ActionsHistory": [
{
"Date": "2026-08-01T12:01:00",
"Type": "Run",
"UserName": "user@example.com"
}
]
}
],
"JobView": "All",
"ContinuationToken": "abc123..."
}
JobState values
| Value | Meaning |
|---|---|
NotStarted
|
Job created but not yet started |
InProgress
|
Job is currently running |
Paused
|
Job is paused |
Cancelling
|
Cancellation in progress |
Cancelled
|
Job was cancelled |
Errored
|
Job ended with errors |
Completed
|
Job finished successfully |
JobType values
| Value | Meaning |
|---|---|
Archive
|
Creates a new ARM archive |
Restore
|
Restores a workspace from an archive |
DatabaseRestore
|
(Deprecated) Restores a workspace from a .BAK file |
Fields
| Field | Type | Notes |
|---|---|---|
JobID
|
string | Use this as {jobId} in subsequent calls. |
JobName
|
string | (No note) |
JobPriority
|
string (enum) | Low, Medium, High. Absent when not set. |
JobState
|
string (enum) | See JobState values table. |
JobType
|
string (enum) | See JobType values table. |
TimeElapsed
|
string | Format: DD:HH:MM:SS. Absent when the job has not started. |
WorkspaceID
|
integer | Applicable to any job type. |
ActionsHistory
|
array | Ordered history of user actions on this job. |
JobID is an opaque string, not necessarily a UUID/GUID. Jobs created by the legacy application can have a plain numeric-looking value (such as "42"); jobs created by the new application use a UUID. Do not parse or validate its format — treat it as an opaque identifier.
Pagination
Repeat the request with the ContinuationToken from the previous response until ContinuationToken is null or absent — that signals the last page.
What changed
| Old behavior | V3 behavior |
|---|---|
| Unlimited single response or SQL table scan | Paginated with ContinuationToken. Default page size: 100. |
ElapsedTime from SQL |
TimeElapsed in response as DD:HH:MM:SS string (absent when not started) |
workspaceId absent or from SQL join |
WorkspaceID present on each job record |
| No action history in list | ActionsHistory included |
TimeElapsed is absent from the response object (not null) when the job has not yet started. Check for field presence before reading it.
What you must do
- Replace any private V2 list call or direct SQL query with
POST /relativity-arm/v3/jobs/list. - Wrap the request body in a top-level
Requestobject (see Request body). - Implement pagination: loop until
ContinuationTokenis absent. - If you were filtering by workspace, use
WorkspaceIDfrom the response client-side.
Job Status
Old approach
Clients queried JobExecutions, JobState, JobStage, and JobHistory tables directly via SQL. Some used the public V1 endpoint:
1
GET /relativity-arm/v1/jobs/{jobID}/status
New endpoint
1
GET /relativity-arm/v3/jobs/{jobId}/status
{jobId} is the JobID value from the list or create-job response.
Response
1
2
3
4
5
6
7
8
9
10
{
"JobId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"JobState": "InProgress",
"TimeElapsed": "00:00:11:38",
"CurrentPhase": {
"Phase": "Executing",
"Status": "InProgress",
"Progress": 0.42
}
}
Fields
| Field | Type | Notes |
|---|---|---|
JobId
|
string | Echo of the path parameter. Opaque string — not necessarily a UUID. See note in List Jobs. |
JobState
|
string (enum) | Same values as in the list endpoint |
TimeElapsed
|
string (TimeSpan) | Absent when the job has not started yet |
CurrentPhase
|
object | Absent when the job has not started yet |
Phase and Stage model
ARM introduced Phases as a higher-level grouping above the legacy Stage concept. A Phase represents a piece of work that must happen in sequence relative to other Phases. For example, Validating always completes before Executing begins. Within a single Phase, multiple Stages may run in parallel. The CurrentPhase object reflects the active Phase and its aggregate progress across all of its concurrent Stages.
CurrentPhase fields
| Field | Type | Notes |
|---|---|---|
Phase
|
string (enum) | Validating, Executing, Reporting |
Status
|
string (enum) | NotStarted, Queued, InProgress, Completed, Errored |
Progress
|
float | Completion fraction 0–1 (mean of the phase's stage progress) |
What changed versus legacy SQL
| Legacy SQL field | V3 status endpoint |
|---|---|
CurrentStageStartTime
|
Removed permanently — no replacement |
JobElapsedTime
|
Available as TimeElapsed |
ArchiveFile / ArchivePath |
Not in status — fetch once from the job detail endpoint |
JobName
|
Not in status — fetch once from the job detail endpoint |
What you must do
- Replace direct SQL polling with
GET /relativity-arm/v3/jobs/{jobId}/status. - Remove all code reading
CurrentStageStartTime— permanently gone. TimeElapsedandCurrentPhaseare absent (not null) when the job has not started. Guard against missing fields.- For
JobNameorArchivePath, make a one-time call to the archive or restore job detail endpoint; these fields do not change during execution.
Job Statistics
Old approach
Clients queried ArchiveComposition (archive jobs) or CompositionValidation (restore jobs) directly via SQL. Some used the private V1 endpoint with a jobExecutionId (an internal integer):
1
GET /v1/jobs/{jobExecutionId}/statistics
New endpoint
1
GET /relativity-arm/v3/jobs/{jobId}/statistics
Takes the JobID from the list or create-job response. No need to look up an execution ID.
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"JobID": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"Migrators": [
{
"DisplayName": "Document Repository Files",
"SourceItemsCount": 48320,
"DestinationItemsCount": 48318,
"MissingItemsCount": 2
},
{
"DisplayName": "Non-Repository Files",
"SourceItemsCount": 5102,
"DestinationItemsCount": 48318,
"MissingItemsCount": null
}
]
}
Migrators entry fields
| Field | Type | Notes |
|---|---|---|
DisplayName
|
string | Human-readable content type name. Use directly for display. |
SourceItemsCount
|
integer | Items in source workspace or archive before the job ran. Absent when not tracked. |
DestinationItemsCount
|
integer | Items that reached the destination, counted after the job finished. Absent when not tracked by this migrator. |
MissingItemsCount
|
integer | Items present in source but not found on disk. Absent when not tracked by this migrator. |
What changed versus V1 private
| Old V1 behavior | V3 behavior |
|---|---|
Called with jobExecutionId (integer) — required a separate lookup step |
Called with JobID — no lookup step needed |
| Single endpoint for archive and restore | Same — one endpoint covers both |
| All migrators returned including those that did not run | Only migrators that produced statistics are included |
ItemTypeName string required client-side mapping |
DisplayName is human-readable, provided directly |
Error responses
| Status | When |
|---|---|
404 Not Found
|
Job does not exist or has been deleted |
400 Bad Request
|
Job exists but has not yet completed — statistics are not available until the job finishes |
What you must do
- Replace the V1 private call and SQL queries with
GET /relativity-arm/v3/jobs/{jobId}/statistics. - Remove the intermediate call to resolve
jobExecutionId— pass theJobIDdirectly. MissingItemsCountmay be absent (not null) for migrators that do not track missing items — guard before reading.400is returned while the job is still running. Show a "not yet available" state rather than an error.
Available Archives for Restore
Old approach
1
2
POST /Relativity.REST/api/Relativity.ARM/Configuration/AvailableArchives (unversioned Kepler)
GET /relativity-arm-private/v2/restore-archive/available-archives (private V2)
New endpoint
1
POST /relativity-arm/v3/restore-jobs/available-archives
Request body
All V3 POST and PUT endpoints wrap their body in a top-level Request object.
1
2
3
4
5
6
{
"Request": {
"ContinuationToken": null,
"PageSize": 25
}
}
| Parameter | Type | Default | Notes |
|---|---|---|---|
continuationToken
|
string | null
|
Opaque token returned by the previous page; omit on the first request |
pageSize
|
integer | 25
|
Items per page. If omitted, the default value of 25 is used. |
Response shape
1
2
3
4
5
6
7
8
9
10
{
"Items": [
{
"FormattedName": "WorkspaceName (1000001 - 26-06-25 12:22:35)",
"FullPath": "\\\\server\\ARM\\1000001_WorkspaceName_20260625122235",
"ArchiveInUse": false
}
],
"ContinuationToken": "opaque-token-for-next-page"
}
What you must do
- Replace the Kepler unversioned call or private V2 call with
POST /relativity-arm/v3/restore-jobs/available-archives. - Wrap the request body in a top-level
Requestobject (see Request body). - If migrating from the unversioned Kepler endpoint: switch from session-token auth to bearer token.
- Paging is now cursor-based: pass the
ContinuationTokenfrom the previous response ascontinuationTokenon the next request. An empty or absentContinuationTokenin the response means there are no more results.
Archive Storage Path Directories
Old approach
1
GET /relativity-arm-private/v3/archive-directories/archive
New endpoint
1
GET /relativity-arm/v3/archive-jobs/directories
Response shape
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"Roots": [
{
"DisplayPath": "\\\\server\\ARM",
"DisplayName": "ARM",
"Children": [
{
"DisplayPath": "\\\\server\\ARM\\subfolder",
"DisplayName": "subfolder"
}
]
}
]
}
What you must do
- Replace the private endpoint URL with
/relativity-arm/v3/archive-jobs/directories. - Response structure (
Roots,DisplayPath,DisplayName,Children) is unchanged. Each child hasDisplayPathandDisplayNameonly — only root and one level of depth are supported. - This endpoint takes no parameters and returns the full directory tree in one response — there is no pagination.
Error responses (all endpoints)
| Status | Meaning |
|---|---|
400 Bad Request
|
Invalid request (such as missing required field, out-of-range PageSize) |
401 Unauthorized
|
Missing or invalid bearer token |
403 Forbidden
|
Valid token but caller does not have access to the requested workspace |
404 Not Found
|
Resource does not exist |
Part 2 migration checklist
Check that you have completed the following:
- Replace all job-list calls (private V2 / SQL) with
POST /relativity-arm/v3/jobs/list. - Wrap all POST request bodies (
/relativity-arm/v3/jobs/list,/relativity-arm/v3/restore-jobs/available-archives) in a top-levelRequestobject. - Implement pagination: loop until
ContinuationTokenis absent. - Update status polling to
GET /relativity-arm/v3/jobs/{jobId}/status. - Remove all references to
CurrentStageStartTime— no replacement. - Guard against absent (not null)
TimeElapsedandCurrentPhasewhen job not yet started. - Guard against absent
TimeElapsedin list response when job not yet started. - Move
JobNameandArchivePathfetch to the job detail endpoint. - Replace V1 statistics call with
GET /relativity-arm/v3/jobs/{jobId}/statistics; switched fromjobExecutionId(integer) toJobID(opaque string). - Remove intermediate
jobExecutionIdlookup step for statistics. - Replace unversioned / private V2
AvailableArchivescall withPOST /relativity-arm/v3/restore-jobs/available-archives. - Replace private archive-directories call with
GET /relativity-arm/v3/archive-jobs/directories. - Remove all direct SQL access to ARM tables (
JobLocation,ArchiveComposition,CompositionValidation). - Switch all authentication to bearer token.
In addition, check that you have completed the steps in ARM API (REST) V1 to V3 migration guide - part 1.
Additional resources
- OpenAPI Specification: See ARM REST API Reference V3 for complete API documentation.
- V3 Interface Definitions: Located in
Relativity.ARM.Services.Interfaces.V3namespace. - Model Definitions: Located in
Relativity.ARM.Services.Interfaces.V3.Modelsnamespace.
Getting help
If you encounter issues during migration or require functionality that is not available in V3:
- Review both migration guides:
- Check ARM REST API Reference V3 for detailed endpoint and model documentation.
- Contact Relativity Support for assistance. Select "Developer Services" as the topic of the ticket.