Send every call below to https://api.mdrn.io with Authorization: Bearer <management token>. A management token comes from the console (Workspace, Tokens) and carries the scopes in the table under each call; the console calls the same routes with its own session. Every call that changes something sends an Idempotency-Key (1 to 128 characters, new for each change, the same when you retry it); a call that edits a versioned resource sends If-Match with the version you read.
Every error is JSON with a code you can test for: Error codes lists them. The API reference says how these pages fit together.
Objects
GET /v1/platform/projects/{projectId}/environments/{environmentId}/objects
List objects
The environment’s objects, ordered by id, in pages of 1 to 200 (default 50): ready ones unless state says otherwise. state=deleted lists objects deleted in the last 30 days and takes a management token; your app’s credential cannot list them. state=lost lists objects MDRN could no longer find when it archived or restored the environment; they cannot be downloaded. A management token can list objects while Objects is switched off or archived, to export them; your app’s credential cannot.
| Scope | Who | Idempotency |
|---|---|---|
objects:read |
Management token or console session | Safe to repeat |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
cursor |
query | no | The nextCursor of the previous page, with the same filter. Read for 24 hours. |
limit |
query | no | 1 to 200; 50 when left out. Type: integer (at least 1, at most 200, default 50). |
state |
query | no | ready (the default), deleted or lost. Type: one of "ready", "deleted", "lost". |
Response 200 application/json The objects.
| Field | Type | Required | Description |
|---|---|---|---|
items |
array of ObjectsObject | yes | |
nextCursor |
string, or null | yes |
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
GET /v1/platform/projects/{projectId}/environments/{environmentId}/objects/{objectId}
Read an object
The object: its name, type, size, SHA-256 and state. A deleted object is answered to a management token only. A management token can read an object while Objects is switched off or archived; your app’s credential cannot.
| Scope | Who | Idempotency |
|---|---|---|
objects:read |
Management token or console session | Safe to repeat |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
objectId |
path | yes | The object’s id. |
Response 200 application/json The object.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
DELETE /v1/platform/projects/{projectId}/environments/{environmentId}/objects/{objectId}
Delete an object
Every link to the object stops working at once and it leaves the list. The object is kept 30 days, counting as stored, in which a management token can restore it; deleting it again does not extend the 30 days.
| Scope | Who | Idempotency |
|---|---|---|
objects:write |
Management token or console session | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
objectId |
path | yes | The object’s id. |
Response 204: no body. Deleted.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
POST /v1/platform/projects/{projectId}/environments/{environmentId}/objects/{objectId}/download-grants
Get a download link
Answers a link (url) whoever holds it can GET for 15 minutes, as often as they like, with a Range header for part of the object, and no credential. For a ready object only. The link stops working when the object is deleted or Objects is switched on or off. While Objects is switched off or archived (an unpaid workspace, during its 90 days), a management token still gets links, so every object can be exported; your app’s credential cannot, nothing is restored or moved by reading, and downloads through those links are not charged.
| Scope | Who | Idempotency |
|---|---|---|
objects:read |
Management token or console session | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
objectId |
path | yes | The object’s id. |
Response 200 application/json The link, with its method, headers and expiry.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
POST /v1/platform/projects/{projectId}/environments/{environmentId}/objects/{objectId}/restore
Restore a deleted object
Brings back an object deleted in the last 30 days (purgeAt still ahead), with the same id, ready again and counting as stored. Management token only; Objects must be on for the environment. Links issued before the deletion stay dead: issue new ones.
| Scope | Who | Idempotency |
|---|---|---|
objects:write |
Management token or console session | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
objectId |
path | yes | The object’s id. |
Response 200 application/json The object, ready again.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
Uploads
POST /v1/platform/projects/{projectId}/environments/{environmentId}/objects/uploads
Start an upload
Opens an upload for one object, with its name, type, size and the SHA-256 of the whole object, which the bytes are checked against before the object becomes ready. Answers 202 with the upload, which MDRN prepares for up to about 5 seconds first, so its state is almost always uploading already; if it is not yet, read it until it is, then send the parts. An upload not completed within 24 hours expires. A project and environment may have at most 100 uploads open at once (429 TOO_MANY_OPEN_UPLOADS) and hold at most 1 TiB, counting what open uploads reserve and deleted objects until they are removed (409 OBJECTS_QUOTA_EXCEEDED). In build mode one object may be at most 100 MB and a project and environment may hold at most 1 GB, counted the same way (402 BUILD_LIMIT_REACHED); nothing is deleted, and going live lifts both.
| Scope | Who | Idempotency |
|---|---|---|
objects:write |
Management token or console session | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
Request body application/json
Response 202 application/json The upload, usually uploading already; if not yet, read it until it is.
Errors: 400, 401, 402, 403, 404, 409, 413, 429, 503, each JSON with a code: Error codes.
GET /v1/platform/projects/{projectId}/environments/{environmentId}/objects/uploads/{uploadId}
Read an upload
The upload: its state, when it expires, partBytes and, once completed, the operationId of the check. Read it to resume an upload after an interruption; the parts already recorded are listed under /parts.
| Scope | Who | Idempotency |
|---|---|---|
objects:read |
Management token or console session | Safe to repeat |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
uploadId |
path | yes | The upload’s id. |
Response 200 application/json The upload.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
DELETE /v1/platform/projects/{projectId}/environments/{environmentId}/objects/uploads/{uploadId}
Abort an upload
Ends the upload: its part links stop working and what was sent is removed. An upload never completed leaves no object.
| Scope | Who | Idempotency |
|---|---|---|
objects:write |
Management token or console session | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
uploadId |
path | yes | The upload’s id. |
Response 204: no body. Aborted.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
POST /v1/platform/projects/{projectId}/environments/{environmentId}/objects/uploads/{uploadId}/complete
Complete an upload
Names every ready part with its number and exact etag, or an empty array for a zero-byte object, and answers 202 with the object and the operationId of the check. MDRN checks the bytes for up to about 5 seconds before it answers, so a small object is usually ready already; while it is verifying, read the object until its state is ready; one whose bytes do not match the SHA-256 or the size is rejected and never served.
| Scope | Who | Idempotency |
|---|---|---|
objects:write |
Management token or console session | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
uploadId |
path | yes | The upload’s id. |
Request body application/json
Response 202 application/json The object: ready, rejected, or still verifying.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
POST /v1/platform/projects/{projectId}/environments/{environmentId}/objects/uploads/{uploadId}/parts
Get a link to upload one part
Answers 202 with a link (url, method PUT, headers) to send one part’s bytes to, with partNumber from 1 and the part’s size. Every part is 16 MiB except the last, which may be shorter. A new link can be asked for while the upload is open, for a part not sent yet or one to send again.
| Scope | Who | Idempotency |
|---|---|---|
objects:write |
Management token or console session | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
uploadId |
path | yes | The upload’s id. |
Request body application/json
Response 202 application/json The link to PUT the part to.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
Operations
GET /v1/platform/projects/{projectId}/environments/{environmentId}/objects/operations/{operationId}
Read an operation
The operation a 202 answered, with its state: accepted, running and retrying are unfinished; failed carries an error code you can act on.
| Scope | Who | Idempotency |
|---|---|---|
objects:read |
Management token or console session | Safe to repeat |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
operationId |
path | yes | The operation’s id, from the answer that started it. |
Response 200 application/json The operation.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
Delete everything
DELETE /v1/platform/projects/{projectId}/environments/{environmentId}/objects/data
Delete every object in the environment
Deletes every ready object, each kept 30 days as after a single deletion, aborts every open upload, and answers 202 with the operation. Management token only. Objects deleted earlier keep their own deadlines.
| Scope | Who | Idempotency |
|---|---|---|
objects:write |
Management token or console session | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
Request body application/json
| Field | Type | Required | Description |
|---|---|---|---|
confirm |
true |
yes | Must be true. |
Response 202 application/json The deletion, accepted.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
Folders
POST /v1/platform/projects/{projectId}/environments/{environmentId}/objects/folders/delete
Delete a folder
Deletes every ready object whose name starts with prefix, each as DELETE /v1/objects/{objectId} does: its links stop working at once and it is kept 30 days, counting as stored, in which a management token can restore it. objects is the count the person confirmed; when the folder holds another number nothing is deleted (409 FOLDER_CHANGED). A folder of more than 10,000 objects is refused (409 FOLDER_TOO_LARGE): delete the folders inside it one at a time. Management token only.
| Scope | Who | Idempotency |
|---|---|---|
objects:write |
Management token or console session | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
Request body application/json
Response 200 application/json The folder, deleted.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
POST /v1/platform/projects/{projectId}/environments/{environmentId}/objects/folders/download-grants
Get download links for a folder
Answers download links for every ready object whose name starts with prefix (a folder ending in /, or empty for the whole environment), 100 per page, ordered by id: send nextCursor back for the next page. Each link is the one POST /v1/objects/{objectId}/download-grants would answer: valid 15 minutes, for one object, and each download through it is an object read like any other. Ask for each page when you are ready to download it, so its links are still valid. Management token only; while Objects is switched off or archived a management token still gets links, so every object can be exported, and those downloads are not charged.
| Scope | Who | Idempotency |
|---|---|---|
objects:read |
Management token or console session | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
Request body application/json
Response 200 application/json One page of download links.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
POST /v1/platform/projects/{projectId}/environments/{environmentId}/objects/folders/move
Move or rename a folder
Renames every ready object whose name starts with from so that it starts with to instead (images/a.png to pictures/a.png): a folder is only the path its objects’ names share. Objects keep their ids, their links and their bytes; nothing is copied, and nothing is charged. It is all or nothing: a folder of more than 10,000 objects is refused (409 FOLDER_TOO_LARGE), as is one with an object still being checked (409 FOLDER_BUSY), and when any new name is already taken by another object nothing is moved (409 FOLDER_CONFLICT, naming the first). to cannot be inside from. An upload still open keeps the name it was started with. Management token only.
| Scope | Who | Idempotency |
|---|---|---|
objects:write |
Management token or console session | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
projectId |
path | yes | The project’s id. |
environmentId |
path | yes | sandbox or production. |
Request body application/json
Response 200 application/json The folder, moved.
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
Other
GET /transfer/{grant}
Download an object
The address in a download link: whoever holds it GETs the object, with no credential, until the link expires; a Range header asks for part of it. The link is for one object and the one method it was issued for, and stops working when the object is deleted or Objects is switched on or off for the environment; a link a management token got while Objects was off works until then.
| Scope | Who | Idempotency |
|---|---|---|
| none | Whoever holds the transfer link | Safe to repeat |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
Range |
header | no | One bytes=start-end, bytes=start- or bytes=-suffix range. A malformed Range, another unit or several ranges are ignored and the whole object is sent (200). |
If-Range |
header | no | The range is served only while this strong entity tag is the object’s ETag; otherwise (a weak tag or a date) the whole object is sent. |
Response 200: no body. The object’s bytes.
Response 206: no body. The byte range asked for, with Content-Range.
Errors: 400, 403, 404, 409, 416, each JSON with a code: Error codes.
PUT /transfer/{grant}
Upload one part
The address in a part link: PUT exactly the part’s bytes, with the headers the link came with, and read the part’s etag from the answer. The link is for one upload, one part and one size, until it expires; a body of another size is 400.
| Scope | Who | Idempotency |
|---|---|---|
| none | Whoever holds the transfer link | No Idempotency-Key |
Request body application/octet-stream
string (binary)
Response 200: no body. Part recorded; the answer carries partNumber, bytes and etag.
Errors: 400, 403, 404, 409, each JSON with a code: Error codes.
Schemas
ObjectsAccepted
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | |
state |
string | yes | |
kind |
string | yes | |
requestId |
string | yes |
ObjectsCompleteRequest
| Field | Type | Required | Description |
|---|---|---|---|
parts |
array of object with partNumber, etag (at most 640 items) |
yes | Every part, with the etag its upload answered; empty for a zero-byte object. |
ObjectsCompletion
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | The object’s id. |
filename |
string | yes | |
contentType |
string | yes | |
bytes |
integer (int64) (at least 0) | yes | |
state |
one of "verifying", "ready", "rejected" |
yes | Where the check had got to when the call answered: usually ready (or rejected) for a small object; verifying while it goes on. |
operationId |
string | yes | The check to follow; the object is ready once it succeeds. |
ObjectsFolderDeleteRequest
| Field | Type | Required | Description |
|---|---|---|---|
prefix |
string (at most 255 characters) | yes | A folder: the path an object’s name starts with, ending in /, such as images/2026/. |
objects |
integer (int64) (at least 0) | yes | How many objects the folder holds, as shown to the person who confirmed the deletion. A folder that holds another number is not deleted (409 FOLDER_CHANGED). |
ObjectsFolderDeleted
| Field | Type | Required | Description |
|---|---|---|---|
deleted |
integer (int64) (at least 0) | yes | How many objects were deleted. |
ObjectsFolderLinks
| Field | Type | Required | Description |
|---|---|---|---|
items |
array of object with id, filename, contentType, bytes, url, expiresAt (at most 100 items) |
yes | |
nextCursor |
string, or null | yes |
ObjectsFolderLinksRequest
| Field | Type | Required | Description |
|---|---|---|---|
prefix |
string (at most 255 characters) | yes | The folder, ending in /, or empty for every object in the environment. |
cursor |
string, or null | no | The nextCursor of the previous page, with the same prefix. Read for 24 hours. |
ObjectsFolderMoveRequest
| Field | Type | Required | Description |
|---|---|---|---|
from |
string (at most 255 characters) | yes | A folder: the path an object’s name starts with, ending in /, such as images/2026/. |
to |
string (at most 255 characters) | yes | Where the folder goes: another folder ending in /, or empty for the top. Not inside from. |
ObjectsFolderMoved
| Field | Type | Required | Description |
|---|---|---|---|
moved |
integer (int64) (at least 0) | yes | How many objects were renamed. |
from |
string | yes | |
to |
string | yes |
ObjectsGrant
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | |
url |
string (uri) | yes | The address to send the request to, with exactly the method and headers given; the whole URL is a credential. |
method |
one of "GET", "PUT" |
yes | |
headers |
empty object | yes | Headers to send with the request. |
expiresAt |
string (date-time) | yes | The link stops working then, 15 minutes after it was issued. |
maxBytes |
integer (int64) (at least 0) | yes |
ObjectsObject
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | |
filename |
string | yes | |
contentType |
string | yes | |
bytes |
integer (int64) (at least 0) | yes | |
sha256 |
string | yes | |
state |
one of "verifying", "ready", "rejected", "deleted", "purging", "purge_failed", "purged", "lost" |
yes | ready: verified and served. rejected: the bytes did not match the SHA-256 or the size; never served. deleted: kept until purgeAt and restorable until then. lost: MDRN could no longer find the object when it archived or restored the environment; it cannot be downloaded. purge_failed: the object’s permanent removal gave up; it is retried three more times, a day apart, and again when every object in the environment is next deleted. A deleted object stops counting as stored when its permanent removal begins, whether or not that succeeds. |
createdAt |
string (date-time) | yes | |
deletedAt |
string (date-time), or null | yes | |
purgeAt |
string (date-time), or null | yes |
ObjectsOperation
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | |
kind |
string | yes | |
state |
string | yes | accepted, running and retrying are unfinished; succeeded and failed are final. |
processedBytes |
integer (int64) (at least 0) | yes | |
processedItems |
integer (int64) (at least 0) | yes | |
attempts |
integer (int64) (at least 0) | yes | |
retryAt |
string (date-time), or null | yes | |
error |
string, or null | yes |
ObjectsPartRequest
| Field | Type | Required | Description |
|---|---|---|---|
partNumber |
integer (at least 1, at most 640) | yes | |
bytes |
integer (int64) (at least 1, at most 16777216) | yes | The part’s size: 16 MiB for every part but the last. |
ObjectsUpload
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | |
objectId |
string | yes | The id the object will have. |
state |
one of "pending", "uploading", "verifying", "complete", "aborted", "expired", "rejected" |
yes | uploading: send the parts. verifying: completed, and the bytes are being checked. expired: not completed within 24 hours. |
expiresAt |
string (date-time) | yes | |
operationId |
string, or null | yes | |
partBytes |
16777216 |
yes | The size of every part but the last, in bytes (16 MiB). |
ObjectsUploadRequest
| Field | Type | Required | Description |
|---|---|---|---|
filename |
string (at least 1 character, at most 255 characters) | yes | The object’s name, at most 255 UTF-8 bytes, no control characters and no invisible direction or zero-width characters (the zero-width joiner is allowed); shown and sent with downloads, never used as a path. |
contentType |
string (at least 1 character, at most 128 characters) | yes | The object’s media type, at most 128 UTF-8 bytes, no control characters. |
bytes |
integer (int64) (at least 0, at most 10737418240) | yes | The object’s size, at most 10 GiB. |
sha256 |
string (matching ^[a-fA-F0-9]{64}$) |
yes | The hex SHA-256 of the whole object; the upload is rejected when the bytes do not match it. |