# Reservoir DDMS (RDDMS) Tutorial 

## Table of Contents

- [Overview](#overview)
  - [Why a dedicated service?](#why-a-dedicated-service)
  - [Capabilities enabled by the Reservoir DDMS](#what-can-i-do)
  - [Ingestion paths](#ingestion-paths)
- [Prerequisites](#prerequisites)
- [Before you begin](#before-you-begin)
- [Concepts](#core-concepts)
  - [DataSpaces](#dataspaces-concept)
  - [Design Principles](#design-principles)
  - [What RDDMS Owns vs References](#owns-vs-references)
  - [OSDU Platform Integration](#osdu-integration)
  - [Authentication](#authentication-concept)
  - [Lifecycle: From Working Model to Published Record](#lifecycle)
- [Architecture](#architecture)
  - [API Surfaces](#api-surfaces)
  - [Use Cases](#use-cases)
- [Quickstart: Write objects workflow](#quickstart)
  - [Step 1: Create a dataspace](#step-1-create-a-dataspace)
  - [Step 2: Start a transaction](#step-2-start-a-transaction)
  - [Step 3: Write objects](#step-3-write-objects)
  - [Step 4: Commit the transaction](#step-4-commit-the-transaction)
  - [Step 5: Verify](#step-5-verify)
- [How-to guides](#api-details)
  - [Resources](#api-resources)
  - [Transactions](#api-transactions)
  - [Write](#api-write)
  - [Manifest](#api-manifest)
- [Additional resources](#additional-resources)
- [API reference](#api-reference)


## Overview

The **Reservoir DDMS** is the OSDU platform service that stores, queries, and serves subsurface reservoir model data - grids, surfaces, properties, structural frameworks, and the relationships between them. It is built on OpenETPServer (C++), with a REST API gateway (`open-etp-client`) that translates HTTP requests into ETP protocol messages and stores data in PostgreSQL.

In one sentence: you upload a reservoir model, RDDMS stores the full graph (not just arrays), makes it queryable, and - when you're ready - registers it in the OSDU catalog for enterprise discovery.

### Why a dedicated service?

RESQML data is not flat records. It's a connected graph where a property array only makes sense with its grid, CRS, unit, and property kind. If you split the graph across services, applications must reassemble meaning from fragments. RDDMS keeps objects, arrays, and relationships together under one transactional service boundary.

| What gets stored together | Why it matters |
|  --- | --- |
| Grid geometry + topology + properties | A PERMX array is meaningless without its grid's cell indexing |
| Representation + interpretation + feature | "Horizon depth surface" needs all three layers for geological context |
| Objects + relationships | Navigating "what depends on this fault?" requires the graph, not flat search |


### Capabilities enabled by the Reservoir DDMS 

![Capabilities enabled by the Reservoir DDMS](/assets/capabilities-diagram.53b6171c29797c44aab1da7a21429908ea6c6adef70fa85c51e7bce3282ad03e.2f1092c4.png)

| Task | How | Example |
|  --- | --- | --- |
| Browse objects | `GET /dataspaces/{ds}/resources` | List all IjkGrids, surfaces, properties in a dataspace |
| Navigate graph | `GET .../resources/{uuid}/targets` | "What properties belong to this grid?" |
| Fetch array data | `GET .../resources/{uuid}/arrays/{path}` | Get PORO values (1.2M floats, <100ms) |
| Snapshot a model | `POST /dataspaces/{ds}/clone` + lock | Freeze model state for decision gate |
| Build OSDU manifest | `POST /manifests/build` | Generate WPC records for catalog indexing |


See [How-to guides](#api-details) for the full documentation of each of these calls.

### Ingestion paths 

All content enters RDDMS through direct API calls. The OSDU catalog can be populated automatically or separately via manifest generation.

| Path | Best for | Details |
|  --- | --- | --- |
| ETP (preferred) | Bulk import, streaming, large arrays, desktop apps | Persistent WebSocket, binary Avro, transactions |
| REST | Web clients, simple integrations, scripting | HTTP/JSON, same PostgreSQL outcome |
| EPC file upload | One-shot model upload with optional auto-catalog | `POST /dataspaces/{ds}/epc/upload?autoIngest=true` - stores + indexes in one call |
| EPC file import (CLI) | Offline bulk import | CLI: `openETPServer put --dataspace "project/v1" --file model.epc --hdf model.h5` |


The EPC upload endpoint supports automatic OSDU catalog registration via the `autoIngest` query parameter:

| Mode | What happens | Data in Search |
|  --- | --- | --- |
| `false` (default) | EPC stored in RDDMS only | Not searchable via OSDU Search |
| `true` / `records` | Manifest built - records pushed to Storage Service | Immediate (~5-30s after upload) |
| `workflow` | Manifest built - submitted to Airflow `Osdu_ingest` DAG | After DAG completes (~30-90s) |


This aligns the RDDMS user experience with other OSDU DDMSes (Seismic, Wellbore, RAFS) where "upload - data appears in search" is a single step.

> **Note:** This tutorial covers only the **REST** path. ETP and EPC file upload/import are listed above for completeness but are not documented in this tutorial - see [Additional resources](#additional-resources) for links.


**What you'll learn**

- What DataSpaces are and how to manage them
- How to write RESQML objects and arrays transactionally via the REST API
- How to browse, query, and navigate the resource graph
- How to build an OSDU manifest to register model content in the OSDU catalog
- How RDDMS relates to the broader OSDU platform (OSDU Search, Entitlements, Storage Service)


**Who it's for**

API consumers who need to ingest, manage, or retrieve subsurface reservoir model data on an OSDU-based platform using the REST interface.

**Expected outcome**

By the end of this tutorial you will have created a dataspace, written RESQML objects within a transaction, committed the transaction, and verified the objects are accessible. You will also understand how to build an OSDU manifest to make your model discoverable via OSDU Search.

> **Note:** This tutorial covers the REST API. For large array I/O (>100k floats), JSON serialization becomes the bottleneck (~5–7× slower than binary Avro). For array-heavy workflows (seismic grids, simulation results), use the EPC upload endpoint or direct ETP.


## Prerequisites

Before making any API calls, ensure the following are in place:

- **OSDU data partition access** - you have access and permissions to the target data partition.
- **Bearer token** - a valid OAuth 2.0 access token. RDDMS inherits OSDU authentication and enforces ACL through the Entitlements service. Every request requires a Bearer token validated against the platform's authorization service.
- **ACL groups configured** - access control list groups are set up for the dataspace you intend to create or access.
- **Legal tags registered** - legal tags are prepared and registered in the partition before promoting data to the OSDU catalog.
- **RESQML objects prepared** - objects to be written must conform to the Energistics RESQML JSON schema, including a `$type` field identifying the Energistics qualified type.


## Before you begin

Replace the following placeholders in every `curl` example throughout this tutorial:

| Placeholder | Example | Description |
|  --- | --- | --- |
| `{root-url}` | `https://<partition>.api.enterprisedata.slb.com` | Base URL of your OSDU environment |
| `{data-partition-id}` | `opendes` | ID of the target data partition |
| `{dataspaceId}` | `demo/Volve` | Two-level path identifying the dataspace (e.g. `project/model-v3`) |
| `{transactionId}` | `1615d8d2-2a2d-482c-885e-14225b89e90c` | UUID returned by `POST /dataspaces/{dataspaceId}/transactions` |
| `{objectUuid}` | `68f2a7d4-f7c1-4a75-95e9-3c6a7029fb23` | UUID of a specific RESQML object within the dataspace |
| `{dataObjectType}` | `eml20.obj_EpcExternalPartReference` | Energistics qualified type of the object (e.g. `resqml20.obj_IjkGridRepresentation`) |
| `{containerUuid}` | `53395ada-6f93-4bac-b506-d45997ded2a2` | UUID of the HDF proxy / array container object |
| `{pathInResource}` | `RESQML/68f2a7d4-f7c1-4a75-95e9-3c6a7029fb23/points_patch0` | Path to the array within the HDF file (as returned by the List arrays endpoint) |


**Required headers**

Every endpoint requires the following headers:

| Header | Required | Description |
|  --- | --- | --- |
| `authorization` | Yes | `Bearer <JWT>` - OAuth 2.0 access token |
| `data-partition-id` | Yes | ID of the target data partition |
| `accept` | Yes | `application/json` for most endpoints |
| `content-type` | Write ops only | `application/json` for request bodies |


For full endpoint definitions, request/response schemas, and parameters, see the [Reservoir DDMS OpenAPI specification](/solutions/reservoir-dms/apis/osdu-reservoir-ddms).

## Concepts 

### DataSpaces 

DataSpaces are the fundamental organisational unit - each is a logically isolated container with its own persistence and access control.

### Design Principles 

| # | Principle | One-liner |
|  --- | --- | --- |
| 1 | Catalog discovers, RDDMS serves | Don't put the model graph in the catalog |
| 2 | Don't break RESQML apart | Objects + arrays + relationships = one service |
| 3 | Values need context | An array without its grid/CRS/units is meaningless |
| 4 | Master data stays in its SoR | Don't duplicate wells/logs/CRS into RDDMS |
| 5 | Reference external data by ID | Snapshot only when reproducibility demands it |
| 6 | WIP is isolated, snapshots are locked | Mutable work → immutable gates |
| 7 | Transactions matter | Multi-object updates commit atomically |
| 8 | Copies are explicit and traceable | Source IDs, versions, lineage, and Activity provenance |
| 9 | Identity is deterministic | Repeated manifest builds are idempotent |


### What RDDMS Owns vs References 

| RDDMS owns (model content) | RDDMS references (from other systems) |
|  --- | --- |
| Grids, surfaces, properties, binary arrays | Wells, wellbores (SDMA / Wellbore DDMS) |
| Structural frameworks, sealed models | Trajectories, logs (Wellbore DDMS) |
| Topology, geometry, relationships | CRS, units (reference-data services) |
| Blocked wells, upscaled properties | Stratigraphic columns (enterprise) |
| Model-specific derived objects | Seismic context (Seismic DDMS) |


### Reference patterns:

| Pattern | When to use | What RDDMS stores |
|  --- | --- | --- |
| Reference only | Master/reference data (wells, CRS, units) | Stable ID + version |
| Snapshot | Reproducibility needed (trajectory used for blocked wells) | Source ref + frozen copy + timestamp + provenance |
| Derived object | Model creates new content (blocked wells, upscaled properties) | Full ownership - it's model content |
| Controlled copy | Data-room/partner/regulatory boundary | Explicit copy with source lineage |
| Cache | Display convenience (well name, field name) | Non-authoritative, source ID retained |


### Practical domain rules:

| Domain | RDDMS stores | RDDMS does NOT store |
|  --- | --- | --- |
| Wells | References, blocked-well representations, grid/well intersections, connection objects | New official well/wellbore records |
| Logs | References, sampled values, upscaled properties, provenance links | Raw/governed logs (stay in Wellbore DDMS) |
| Stratigraphy | References, model-time stratigraphic version | Enterprise stratigraphic columns |
| CRS/Units | References, cached display values (non-authoritative) | CRS/unit definitions |
| Ensembles/FMU | Selected model objects, promoted summaries, P10/P50/P90 | Raw FMU ensemble outputs (stay in results store) |


### OSDU Platform Integration 

| OSDU Service | How RDDMS Uses It |
|  --- | --- |
| Authorization / Entitlements | ACL enforcement on every request, group-based access control |
| Partition Service | Maps partitions to PostgreSQL database instances |
| Storage Service | Target for manifest ingestion - WPC records indexed in catalog |
| Search Service | Catalog records become discoverable via kind, text, and spatial queries |
| Schema Service | Validates WPC schemas on ingestion |
| Workflow / Project Service | RDDMS dataspaces serve as project-scoped data stores |


### Authentication 

RDDMS inherits OSDU authentication (OAuth 2.0) and enforces ACL through Entitlements. Every request carries a Bearer token validated against the platform's authorization service.

### Lifecycle: From Working Model to Published Record 

| Step | Who |
|  --- | --- |
| 1. Write | Modeller |
| 2. QC | Reviewer |
| 3. Lock | Project lead |
| 4. Manifest | System / lead |
| 5. Catalog | - |
| 6. Gate | Decision maker |


[Back to table of contents](#TOC)

## Architecture 

![Architecture](/assets/architecture-diagram.0ea38da83bc1c672fa9fe94d95ed0a1b149ce7691803eca6563bc4a6002f571e.2f1092c4.png)

The open-etp-server is the authoritative data owner. The REST client layer is a gateway that translates HTTP requests into ETP protocol messages.

- Manifest Builder is part of open-etp-client - it reads objects from the ETP server and converts them to OSDU WPC records


### API Surfaces 

| API | Protocol | Best For | Spec |
|  --- | --- | --- | --- |
| ETP 1.2 | WebSocket | Bulk import/export, streaming, application integration | ETP spec (Energistics) |
| REST | HTTP/JSON | Browse, search, CRUD, manifest generation, web integration | OpenAPI spec |
| GraphQL-ready | HTTP/JSON | Deep graph queries, multi-object resolution, custom front-ends | open-etp-client README |
| OSDU Catalog | HTTP/JSON | Kind-based search, spatial queries, cross-service discovery | Via manifest builder → Storage API → Search API |


### Use Cases 

#### Reservoir Modelling Workflow 

![Reservoir Modelling Workflow](/assets/reservoir-modelling-workflow.87e4b25bc933a1c6e9beb83e33ac099de5d2f61ffb52fb44e60065db2d0bbe75.2f1092c4.png)

#### Seismic Interpretation 

- Interpreter writes surfaces (Grid2dRepresentation + Z-arrays) to RDDMS
- Manifest builder generates StructureMap:1.0.0 WPC in OSDU (metadata only, no Z-values)
- Consumers discover via Search, fetch data from RDDMS via DDMSDatasets[] URI


#### Cross-DDMS Interoperation 

![Cross-DDMS Interoperation](/assets/cross-ddms-interoperation.73c9dc72e29388fdf55e6f9e7158f92ca279ba1c2d1a45fd3bef6fc0602acf83.2f1092c4.png)

- Blocked wells: model-derived content stored in RDDMS, referencing wellbore data by ID (WITSML integration)
- Seismic: interpretation surfaces in RDDMS reference seismic volumes in Seismic DDMS (data model)
- Workflow Service: orchestrates multi-DDMS workflows, RDDMS provides working storage


[Back to table of contents](#TOC)

## Quickstart: Write objects workflow

**Goal:** Create a dataspace, write RESQML objects within a transaction, commit the transaction, and list the objects in the dataspace.

This quickstart uses the **REST scripting path** (web clients, simple integrations). The preferred ingestion paths for bulk or production workflows are ETP (WebSocket) and EPC file upload — see the [How-to guides](#api-details) for those endpoints.

Create a WIP RDDMS dataspace with ACL groups configured (see Prerequisites), then write RESQML objects via REST. Object modification should be done within a transaction: create a transaction, write objects with the returned `transactionId`, then commit using the ID provided by Post.

Content ingestion and catalog indexing are independent. Catalog indexing is only needed for OSDU Search visibility. To generate WPC records, use `POST /manifests/build` after the objects are committed (see [Build an OSDU manifest](#manifest-build)).

| Step | Action |
|  --- | --- |
| 1 | Create a dataspace |
| 2 | Start a transaction |
| 3 | Write objects (within the transaction) |
| 4 | Commit the transaction |
| 5 | Verify: list objects in the dataspace |


### Step 1: Create a dataspace

The dataspace should be created with ACL groups configured. ACL groups are managed via the OSDU Entitlements service (a prerequisite — see [Prerequisites](#prerequisites)) and are not part of the `POST /dataspaces` request body.

**Procedure**

```
POST /dataspaces
```

details
summary
curl
```bash
curl --request POST \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'content-type: application/json' \
  --header 'data-partition-id: {data-partition-id}' \
  --data '[
    {
      "DataspaceId": "{dataspaceId}",
      "Path": "{dataspaceId}"
    }
  ]'
```

**Expected result**

The response (HTTP 201) contains an array of EML URIs for each created dataspace:

```json
["eml:///dataspace('{dataspaceId}')"]
```

**Verify**

Use the GET call below to confirm the dataspace is accessible:

details
summary
curl
```bash
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/info' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

The response (`DataspaceDto`) includes `uri`, `path`, `storeCreated`, `storeLastWrite`, and `customData`. Confirm the expected `path` matches the dataspace you created.

### Step 2: Start a transaction

Object modification should be done within a transaction.

**Procedure**

```
POST /dataspaces/{dataspaceId}/transactions
```

details
summary
curl
```bash
curl --request POST \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/transactions' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'content-type: application/json' \
  --header 'data-partition-id: {data-partition-id}' \
  --data '{
    "TimeoutPeriod": 300,
    "Retries": 6
  }'
```

**Expected result**

The response contains a `transactionId`. Copy this value - you will pass it as the `transactionId` query parameter in Steps 3 and 4.

### Step 3: Write objects

**Procedure**

```
PUT /dataspaces/{dataspaceId}/resources?transactionId={transactionId}
```

details
summary
curl
```bash
# Replace {transactionId} with the value from Step 2
curl --request PUT \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources?transactionId={transactionId}' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'content-type: application/json' \
  --header 'data-partition-id: {data-partition-id}' \
  --data '[
    {
      "Uuid": "68f2a7d4-f7c1-4a75-95e9-3c6a7029fb23",
      "$type": "eml20.obj_EpcExternalPartReference",
      "SchemaVersion": "2.0.0.20140822",
      "Citation": {
        "Title": "My Object",
        "Originator": "me",
        "Creation": "2024-01-01T00:00:00Z",
        "Format": "[Vendor:Software:1.0]"
      }
    }
  ]'
```

Each JSON object must include the required fields `Uuid`, `$type`, `SchemaVersion`, and `Citation`. Objects should conform to the Energistics JSON schema defined for their type. Optional per-object metadata can be added inside a `_ResourceCustomData` field.

**Expected result**

The objects are created within the open transaction.

### Step 4: Commit the transaction

**Procedure**

```
PUT /dataspaces/{dataspaceId}/transactions/{transactionId}
```

details
summary
curl
```bash
# Replace {transactionId} with the value from Step 2
curl --request PUT \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/transactions/{transactionId}' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

**Expected result**

The transaction is committed using the ID returned in Step 2.

### Step 5: Verify

**Procedure**

List all objects in the dataspace to confirm the committed objects are visible.

details
summary
curl
```bash
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources/all?$top=10' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

**Expected result**

The response lists resources in the dataspace. Output can be paginated. Confirm that the object written in Step 3 (`eml20.obj_EpcExternalPartReference`, UUID `68f2a7d4-f7c1-4a75-95e9-3c6a7029fb23`) is visible.

> **Next step:** To make this model discoverable via OSDU Search, use `POST /manifests/build` to generate an OSDU manifest (see [Build an OSDU manifest](#manifest-build) in the How-to guides).


[Back to table of contents](#TOC)

## How-to guides 

For full endpoint definitions, request/response schemas, and parameters, see the [Reservoir DDMS OpenAPI specification](/solutions/reservoir-dms/apis/osdu-reservoir-ddms).

| Level | What you get | How |
|  --- | --- | --- |
| Object list | Types and resources in a dataspace | `GET /dataspaces/{ds}/resources/{type}` |
| Single object | Full RESQML XML/JSON | `GET .../resources/{uuid}` |
| Object graph | Navigate relationships | `GET .../resources/{uuid}/targets` / `sources` |
| Binary arrays | Grid geometry, property values | `GET .../resources/{uuid}/arrays/{path}` |


### Resources 

All `Resources` endpoints require the `Authorization: Bearer <token>` and `data-partition-id` headers described in [REST docs](/solutions/reservoir-dms/apis/osdu-reservoir-ddms).

#### List dataspaces 

List the dataspaces available in a server. Output can be paginated.

```
GET /dataspaces
```

| Parameter | Description |
|  --- | --- |
| $skip | ODATA Pagination: Index of first item returned. [0..100000] |
| $top | ODATA Pagination: Maximum number of items returned. [1..10000] |
| storeLastWriteFilter | If present, only the object modified after given date will be listed. |


details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces?$skip=0&$top=10' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Get dataspace info 

Get all information about a dataspace. The response (`DataspaceDto`) includes `storeCreated`, `storeLastWrite`, and `customData` (extra metadata associated to the dataspace).

```
GET /dataspaces/{dataspaceId}/info
```

details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/info' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Lock a dataspace 

Sets a dataspace read-only.

```
POST /dataspaces/{dataspaceId}/lock
```

details
summary
curl
```
curl --request POST \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/lock' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Unlock a dataspace 

Sets a dataspace back to read-write.

```
DELETE /dataspaces/{dataspaceId}/lock
```

details
summary
curl
```
curl --request DELETE \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/lock' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### List object types in a dataspace 

List the types present in the dataspace, and the number of items for each type.

```
GET /dataspaces/{dataspaceId}/resources
```

| Parameter | Description |
|  --- | --- |
| $skip | ODATA Pagination: Index of first item returned. [0..100000] |
| $top | ODATA Pagination: Maximum number of items returned. [1..10000] |
| transactionId | Identify current transaction |


details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### List all resources 

Lists all resources in a dataspace. Output can be paginated and filtered by types, content, and last update time.

```
GET /dataspaces/{dataspaceId}/resources/all
```

| Parameter | Description |
|  --- | --- |
| $skip | ODATA Pagination: Index of first item returned. [0..100000] |
| $top | ODATA Pagination: Maximum number of items returned. [1..10000] |
| $filter | ODATA filter using XPath syntax |
| storeLastWriteFilter | If present, only the object modified after given date will be listed. |
| dataObjectTypes | If present, only objects with given types will be listed. Provided as comma separated list. |
| countObjects | If true, the source and target count will be computed for each resource. |
| transactionId | Identify current transaction |


details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources/all?$top=10' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### List resources by type 

Restricts the resources list to a given type. Output can be paginated and filtered by content and last update time.

```
GET /dataspaces/{dataspaceId}/resources/{dataObjectType}
```

| Parameter | Description |
|  --- | --- |
| $skip | ODATA Pagination: Index of first item returned. [0..100000] |
| $top | ODATA Pagination: Maximum number of items returned. [1..10000] |
| $filter | ODATA filter using XPath syntax |
| storeLastWriteFilter | If present, only the object modified after given date will be listed. |
| countObjects | If true, the source and target count will be computed for each resource. |
| transactionId | Identify current transaction |


details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources/resqml20.obj_ContinuousProperty?$top=10' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Get object content 

Get the actual content of a data object formatted as xml or json.

```
GET /dataspaces/{dataspaceId}/resources/{dataObjectType}/{guid}
```

| Parameter | Description |
|  --- | --- |
| $format | Expected return format (`xml` or `json`, default `json`) |
| referencedContent | If true, includes the content of referenced objects (default `true`) |
| arrayValues | If true, includes the content of referenced objects and the content of array values (default `false`) |
| arrayMetadata | If true, includes the content of referenced objects and includes the metadata of arrays such as size and type (default `false`) |
| version | Optional version of the object |
| transactionId | Identify current transaction |


details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources/resqml20.obj_ContinuousProperty/{objectUuid}' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Get content of multiple objects 

Get the actual content of a data objects using a list of uris, formatted as xml or json.

```
POST /dataspaces/multi-resources/get-content
```

| Parameter | Description |
|  --- | --- |
| $format | Expected return format (`xml` or `json`, default `json`) |
| referencedContent | If true, includes the content of referenced objects (default `true`) |
| arrayValues | If true, includes the content of referenced objects and the content of array values (default `false`) |
| arrayMetadata | If true, includes the content of referenced objects and includes the metadata of arrays such as size and type (default `false`) |


details
summary
curl
```
curl --request POST \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/multi-resources/get-content' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'content-type: application/json' \
  --header 'data-partition-id: {data-partition-id}' \
  --data "{
    \"uris\": [
      \"eml:///dataspace('{dataspaceId}')/resqml20.obj_TriangulatedSetRepresentation(a3f31b20-c93a-4682-8f6c-71be087202a4)\",
      \"eml:///dataspace('{dataspaceId}')/resqml20.obj_ContinuousProperty({objectUuid})\"
    ]
  }"
```

#### List sources 

Lists all resources referencing a given resource. Referencing can be recursive with a depth greater than 1.

```
GET /dataspaces/{dataspaceId}/resources/{dataObjectType}/{guid}/sources
```

| Parameter | Description |
|  --- | --- |
| depth | Number of recursive levels when looking for relations [1..1000] |
| dataObjectTypes | If present, only objects with given types will be listed. Provided as comma separated list. |
| countObjects | If true, the source and target count will be computed for each resource. |
| $skip / $top | ODATA Pagination |
| $filter | ODATA filter using XPath syntax |
| storeLastWriteFilter | If present, only the object modified after given date will be listed. |
| transactionId | Identify current transaction |


details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources/resqml20.obj_ContinuousProperty/{objectUuid}/sources?depth=1' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### List targets 

Lists all resources referenced by a given resource. Referencing can be recursive with a depth greater than 1.

```
GET /dataspaces/{dataspaceId}/resources/{dataObjectType}/{guid}/targets
```

| Parameter | Description |
|  --- | --- |
| depth | Number of recursive levels when looking for relations [1..1000] |
| dataObjectTypes | If present, only objects with given types will be listed. Provided as comma separated list. |
| countObjects | If true, the source and target count will be computed for each resource. |
| $skip / $top | ODATA Pagination |
| $filter | ODATA filter using XPath syntax |
| storeLastWriteFilter | If present, only the object modified after given date will be listed. |
| transactionId | Identify current transaction |


details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources/resqml20.obj_ContinuousProperty/{objectUuid}/targets?depth=1' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Graph of all resources 

Returns the relationships between all resources in a dataspace as a graph: a `resources` array plus a `links` array, each link carrying a `source` URI, a `target` URI, and a `path` (path inside the source referencing the target).

```
GET /dataspaces/{dataspaceId}/graph/all
```

Accepts the same parameters as [List all resources](#res-list-resources).

details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/graph/all?$top=10' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Graph of sources 

Returns the same information as [List sources](#res-list-sources), as a graph: a `resources` array plus a `links` array, each link carrying a `source` URI, a `target` URI, and a `path` (path inside the source referencing the target).

```
GET /dataspaces/{dataspaceId}/graph/{dataObjectType}/{guid}/sources
```

Accepts the same parameters as [List sources](#res-list-sources).

details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/graph/resqml20.obj_ContinuousProperty/{objectUuid}/sources?depth=1' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Graph of targets 

Returns the same information as [List targets](#res-list-targets), as a graph: a `resources` array plus a `links` array, each link carrying a `source` URI, a `target` URI, and a `path` (path inside the source referencing the target).

```
GET /dataspaces/{dataspaceId}/graph/{dataObjectType}/{guid}/targets
```

Accepts the same parameters as [List targets](#res-list-targets).

details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/graph/resqml20.obj_ContinuousProperty/{objectUuid}/targets?depth=1' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### List arrays 

Gets the description of all the arrays (type and dimensions) referenced by a data object.

```
GET /dataspaces/{dataspaceId}/resources/{dataObjectType}/{guid}/arrays
```

| Parameter | Description |
|  --- | --- |
| version | Optional version of the object |


details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources/{dataObjectType}/{containerUuid}/arrays' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Get array metadata 

Returns type and dimension of one array.

```
GET /dataspaces/{dataspaceId}/resources/{dataObjectType}/{guid}/arrays/{pathInResource}/metadata
```

| Parameter | Description |
|  --- | --- |
| version | Optional version of the object |
| transactionId | Identify current transaction |


details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources/{dataObjectType}/{containerUuid}/arrays/{pathInResource}/metadata' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Get array content 

Returns the array content. For large arrays, it is recommended to use `starts` and `counts` and get the array by slices - they need to be used together or not at all.

```
GET /dataspaces/{dataspaceId}/resources/{dataObjectType}/{guid}/arrays/{pathInResource}
```

| Parameter | Description |
|  --- | --- |
| starts | When selecting a subarray, start of the slice in each dimension |
| counts | When selecting a subarray, number of items in each dimension |
| version | Optional version of the object |
| transactionId | Identify current transaction |


details
summary
curl
```
curl --request GET \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources/{dataObjectType}/{containerUuid}/arrays/{pathInResource}?starts=10,2&counts=4,1' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

[Back to table of contents](#TOC)

### Transactions 

Creating, updating, or deleting objects and arrays is done through the `PUT`/`DELETE` operations detailed in [Write](#api-write). The `Create or update objects` operation's description states: "Object modification should be done within a transaction."

#### Start a transaction 

Create new transaction. Returns a transaction ID that can be used to commit (Put) or rollback (Delete). Transaction will be automatically rolled back after the timeout period if no new transaction messages occur. Only one transaction can be active at a time for a given dataspace.

```
POST /dataspaces/{dataspaceId}/transactions
```

| Body field | Description |
|  --- | --- |
| TimeoutPeriod | Time in seconds before transaction is automatically rolled back (default 300) |
| Retries | If dataspace busy, attempt to create transaction this number of times (default 6) |


details
summary
curl
```
curl --request POST \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/transactions' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'content-type: application/json' \
  --header 'data-partition-id: {data-partition-id}' \
  --data '{
    "TimeoutPeriod": 1200,
    "Retries": 6
  }'
```

The returned `transactionId` is then accepted as a `transactionId` query parameter ("Identify current transaction") on the `Write` endpoints below, and on the `Resources` read endpoints described earlier.

#### Commit a transaction 

Commit a transaction using the ID provided by Post.

```
PUT /dataspaces/{dataspaceId}/transactions/{transactionId}
```

details
summary
curl
```
curl --request PUT \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/transactions/{transactionId}' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Rollback a transaction 

Rollback a transaction using the ID provided by Post.

```
DELETE /dataspaces/{dataspaceId}/transactions/{transactionId}
```

details
summary
curl
```
curl --request DELETE \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/transactions/{transactionId}' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

[Back to table of contents](#TOC)

### Write 

All `Write` endpoints require the `Authorization: Bearer <token>` and `data-partition-id` headers described in [REST docs](/solutions/reservoir-dms/apis/osdu-reservoir-ddms).

#### Create dataspaces 

Create new dataspaces.

```
POST /dataspaces
```

The request body is a JSON array of dataspace descriptions, each requiring a `DataspaceId`, with optional `Path` and `CustomData` fields.

details
summary
curl
```
curl --request POST \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'content-type: application/json' \
  --header 'data-partition-id: {data-partition-id}' \
  --data '[
    {
      "DataspaceId": "{dataspaceId}",
      "Path": "{dataspaceId}"
    }
  ]'
```

#### Delete a dataspace 

Delete existing dataspace.

```
DELETE /dataspaces/{dataspaceId}
```

details
summary
curl
```
curl --request DELETE \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Clone a dataspace 

Duplicate a dataspace. The request body carries the `DataspaceId`, `Path`, and `CustomData` of the new dataspace being created.

```
POST /dataspaces/{dataspaceId}/clone
```

details
summary
curl
```
curl --request POST \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/clone' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'content-type: application/json' \
  --header 'data-partition-id: {data-partition-id}' \
  --data '{
    "DataspaceId": "{dataspaceId}-clone",
    "Path": "{dataspaceId}-clone"
  }'
```

#### Create or update objects 

Create new objects by providing their content as a JSON array. Each JSON object should conform to the Energistics JSON schema defined for that type, including a `$type` field that is an Energistics qualified type when needed. Extra metadata on the resource representing the object can be added inside a `_ResourceCustomData` field of each object, its value being a JSON object of key-value pairs. Object modification should be done within a transaction (see [Transactions](#api-transactions)).

```
PUT /dataspaces/{dataspaceId}/resources
```

| Parameter | Description |
|  --- | --- |
| transactionId | Identify current transaction |


details
summary
curl
```
curl --request PUT \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources?transactionId={transactionId}' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'content-type: application/json' \
  --header 'data-partition-id: {data-partition-id}' \
  --data '[
    {
      "Uuid": "68f2a7d4-f7c1-4a75-95e9-3c6a7029fb23",
      "$type": "eml20.obj_EpcExternalPartReference",
      "SchemaVersion": "2.0.0.20140822",
      "Citation": {
        "Title": "My Object",
        "Originator": "me",
        "Creation": "2024-01-01T00:00:00Z",
        "Format": "[Vendor:Software:1.0]"
      }
    }
  ]'
```

#### Delete an object 

Delete existing object.

```
DELETE /dataspaces/{dataspaceId}/resources/{dataObjectType}/{guid}
```

| Parameter | Description |
|  --- | --- |
| version | Optional version of the object |
| transactionId | Identify current transaction |


details
summary
curl
```
curl --request DELETE \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources/resqml20.obj_ContinuousProperty/{objectUuid}?transactionId={transactionId}' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'data-partition-id: {data-partition-id}'
```

#### Create or update a data array 

Create or update data array to attach to existing object. When `Starts` and `Counts` are present, it updates a subarray. When `Data` is not present, it creates an empty array, otherwise the data can be provided as either an array of numbers or a base64-encoded string. Should be done within a transaction.

```
PUT /dataspaces/{dataspaceId}/resources/arrays
```

| Parameter | Description |
|  --- | --- |
| transactionId | Identify current transaction |


The request body is a JSON array of array descriptors, each with a `ContainerType` (type of the array container).

details
summary
curl
```
curl --request PUT \
  --url '{root-url}/api/reservoir-ddms/v2/dataspaces/{dataspaceId}/resources/arrays?transactionId={transactionId}' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'content-type: application/json' \
  --header 'data-partition-id: {data-partition-id}' \
  --data '[
    {
      "ContainerType": "eml20.obj_EpcExternalPartReference"
    }
  ]'
```

[Back to table of contents](#TOC)

### Manifest 

All `Manifest` endpoints require the `Authorization: Bearer <token>` and `data-partition-id` headers.

#### Build an OSDU manifest 

Create the OSDU manifest for several resources.

```
POST /manifests/build
```

Request body (`ManifestInputDto`):

| Field | Description |
|  --- | --- |
| uris | Uris of resources or dataspaces to generate a manifest for. Required. |
| typePatterns | Energistics types to restrict search against when indexing entire dataspaces, accept `*` and `.` wildcard. |
| technicalAssurances | Technical Assurance information - an array of objects, each requiring a `TechnicalAssuranceTypeID`, with optional `Comment`, `EffectiveDate`, `Reviewers`, `AcceptableUsage`, and `UnacceptableUsage`. |
| createMissingReferences | If true, an entry corresponding to all references not currently present in OSDU storage will be added, else missing references will be added to an error list. Defaults to `true`. |
| tags | OSDU tags information to apply. |


The response is the OSDU manifest document (`kind: osdu:wks:Manifest:1.0.0`).

details
summary
curl
```
curl --request POST \
  --url '{root-url}/api/reservoir-ddms/v2/manifests/build' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer <JWT>' \
  --header 'content-type: application/json' \
  --header 'data-partition-id: {data-partition-id}' \
  --data "{
    \"uris\": [
      \"eml:///dataspace('{dataspaceId}')/resqml20.obj_TriangulatedSetRepresentation(a3f31b20-c93a-4682-8f6c-71be087202a4)\",
      \"eml:///dataspace('{dataspaceId}')/resqml20.obj_ContinuousProperty({objectUuid})\"
    ]
  }"
```

[Back to table of contents](#TOC)

## Additional resources 

- [OSDU-etp-client](https://community.opengroup.org/osdu/tutorials/reservoirddms-tutorial/-/tree/main/02-OSDU-etp-client): Set up and start using the ETP client.
- [RESQML 2.0.1 developer guide](https://energistics.org): Standard reference for the RESQML data model.
- [ETP 1.2 developer guide](https://energistics.org): Standard reference for the Energistics Transfer Protocol.
- [ReservoirDDMS-Tutorial](https://community.opengroup.org/osdu/tutorials/reservoirddms-tutorial) and [Reservoir DDMS home](https://community.opengroup.org/osdu/platform/domain-data-mgmt-services/reservoir/home): Community tutorial and wiki this documentation was adapted from.


## API reference

For full endpoint definitions, request/response schemas, and parameters, see:

[Reservoir DDMS OpenAPI specification](/solutions/reservoir-dms/apis/osdu-reservoir-ddms)

Use the tutorial for workflows, use the spec for exact contract details.

[Back to table of contents](#TOC)