Skip to content

Workspace API for CI/CD

Use the Workspace API to read and change files in a DATAMIMIC project from CI/CD. It is the canonical 4.0 replacement for the deprecated /files routes.

Authentication

Create a project access token for the target project and send it as a Bearer token:

1
2
3
4
export DATAMIMIC_URL="https://datamimic.example.com"
export PROJECT_ID="your-project-id"
export PROJECT_TOKEN="your-project-access-token"
export CLIENT_BINDING="ci-${CI_PIPELINE_ID:-local}"

The token can access only its own project. Treat it as a secret. X-DATAMIMIC-Client-Binding is not a secret; keep one stable value for the lifetime of one CI job or client process.

What the concurrency headers mean

You need no lock to read or create a new file. Existing files are protected by three values:

Value Meaning How to obtain it
X-DATAMIMIC-Client-Binding Identifies this CI job or client process Choose one stable value for the job
ETag / If-Match Identifies the file version you read Read it from /workspace/tree or the file response
X-DATAMIMIC-Lock-Generation Proves this client owns the current lock Copy generation from /workspace/locks/acquire

This prevents two clients from silently overwriting each other. A stale ETag or lock generation fails safely.

Public endpoints

Method Endpoint Use
GET /workspace/tree List visible files, sources, ETags, and read-only state
GET /workspace/files/{path} Read one Base64-encoded file and its ETag
PUT /workspace/files/{path} Create or update one file
POST /workspace/locks/acquire Acquire the lock generation required for an existing-file mutation
POST /workspace/locks/heartbeat Renew a long-held lock
POST /workspace/locks/release Release the lock after the last mutation

List and read files

The tree is source-aware. workspace_path is the visible path, source_project_id identifies the real source, and linked global winners are readonly.

1
2
3
curl --fail-with-body --silent \
  "$DATAMIMIC_URL/api/v2/projects/$PROJECT_ID/workspace/tree" \
  -H "Authorization: Bearer $PROJECT_TOKEN"

File content is returned as Base64-encoded text/plain. The response also includes its current ETag header.

1
2
3
4
5
6
FILE_PATH="models/customer.xml"

curl --fail-with-body --silent \
  "$DATAMIMIC_URL/api/v2/projects/$PROJECT_ID/workspace/files/$FILE_PATH" \
  -H "Authorization: Bearer $PROJECT_TOKEN" \
  | base64 --decode > customer.xml

Create a file atomically

If-None-Match: * is mandatory for create-only behavior. If the path already exists, the request fails instead of overwriting it.

1
2
3
4
5
6
curl --fail-with-body --request PUT \
  "$DATAMIMIC_URL/api/v2/projects/$PROJECT_ID/workspace/files/$FILE_PATH" \
  -H "Authorization: Bearer $PROJECT_TOKEN" \
  -H "X-DATAMIMIC-Client-Binding: $CLIENT_BINDING" \
  -H 'If-None-Match: *' \
  -F '[email protected];type=application/xml'

Update a file safely

An update requires both the current ETag and an owned lock generation. Read the ETag from the tree, then acquire the lock:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
ETAG=$(curl --fail-with-body --silent \
  "$DATAMIMIC_URL/api/v2/projects/$PROJECT_ID/workspace/tree" \
  -H "Authorization: Bearer $PROJECT_TOKEN" \
  | jq --raw-output --arg path "$FILE_PATH" \
    '.entries[] | select(.workspace_path == $path) | .etag')

GENERATION=$(curl --fail-with-body --silent --request POST \
  "$DATAMIMIC_URL/api/v2/projects/$PROJECT_ID/workspace/locks/acquire" \
  -H "Authorization: Bearer $PROJECT_TOKEN" \
  -H "X-DATAMIMIC-Client-Binding: $CLIENT_BINDING" \
  -H 'Content-Type: application/json' \
  --data "{\"path\":\"$FILE_PATH\"}" \
  | jq --raw-output '.generation')

curl --fail-with-body --request PUT \
  "$DATAMIMIC_URL/api/v2/projects/$PROJECT_ID/workspace/files/$FILE_PATH" \
  -H "Authorization: Bearer $PROJECT_TOKEN" \
  -H "X-DATAMIMIC-Client-Binding: $CLIENT_BINDING" \
  -H "X-DATAMIMIC-Lock-Generation: $GENERATION" \
  -H "If-Match: $ETAG" \
  -F '[email protected];type=application/xml'

For a long-running job, renew the lease through /workspace/locks/heartbeat. Release it when no further mutation is planned:

1
2
3
4
5
6
7
curl --fail-with-body --request POST \
  "$DATAMIMIC_URL/api/v2/projects/$PROJECT_ID/workspace/locks/release" \
  -H "Authorization: Bearer $PROJECT_TOKEN" \
  -H "X-DATAMIMIC-Client-Binding: $CLIENT_BINDING" \
  -H "X-DATAMIMIC-Lock-Generation: $GENERATION" \
  -H 'Content-Type: application/json' \
  --data "{\"path\":\"$FILE_PATH\"}"

What is intentionally not public

Template helpers, bulk wizard flows, entry move/delete, directory presentation metadata, entry visibility, lock takeover, durable upload/database operations, and the collaboration WebSocket belong to first-party product workflows. They are not published in /external_openapi.json.

If a CI job receives a foreign-lock conflict, it must stop. It must not take over another client's lock automatically because that client can have unsaved work.