Your backend calls every route below on your app’s own origin, through env.MDRN_PRODUCTS.fetch, with Authorization: Bearer and the credential in MDRN_OBJECTS_TOKEN; the address is in MDRN_OBJECTS_URL. A browser never calls them: a call without the credential is answered 404 CALL_FROM_BACKEND. Every call that is not a GET 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. Apps with a backend says where the variables come from.
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/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 |
Your backend’s product credential, or a management token | Safe to repeat |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
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/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 |
Your backend’s product credential, or a management token | Safe to repeat |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
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/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 |
Your backend’s product credential, or a management token | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
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/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 |
Your backend’s product credential, or a management token | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
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.
Uploads
GET /v1/objects/uploads
List uploads
The environment’s uploads, in pages of 1 to 200 (default 50); a cursor works for 24 hours.
| Scope | Who | Idempotency |
|---|---|---|
objects:read |
Your backend’s product credential, or a management token | Safe to repeat |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
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). |
Response 200 application/json The uploads.
| Field | Type | Required | Description |
|---|---|---|---|
items |
array of ObjectsUpload | yes | |
nextCursor |
string, or null | yes |
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
POST /v1/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 |
Your backend’s product credential, or a management token | Idempotency-Key required |
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/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 |
Your backend’s product credential, or a management token | Safe to repeat |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
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/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 |
Your backend’s product credential, or a management token | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
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/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 |
Your backend’s product credential, or a management token | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
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.
GET /v1/objects/uploads/{uploadId}/parts
List the parts of an upload
The parts recorded so far, each with its number, size, etag and state. Only ready parts can be named when completing, and a part’s number cannot change once it is recorded.
| Scope | Who | Idempotency |
|---|---|---|
objects:read |
Your backend’s product credential, or a management token | Safe to repeat |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
uploadId |
path | yes | The upload’s id. |
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). |
Response 200 application/json The parts.
| Field | Type | Required | Description |
|---|---|---|---|
items |
array of ObjectsPart | yes | |
nextCursor |
string, or null | yes |
Errors: 400, 401, 403, 404, 409, 413, 503, each JSON with a code: Error codes.
POST /v1/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 |
Your backend’s product credential, or a management token | Idempotency-Key required |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
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/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 |
Your backend’s product credential, or a management token | Safe to repeat |
Parameters
| Name | In | Required | Description |
|---|---|---|---|
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.
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
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. |
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 |
ObjectsPart
| Field | Type | Required | Description |
|---|---|---|---|
partNumber |
integer (at least 1, at most 640) | yes | |
bytes |
integer (int64) (at least 0) | yes | |
etag |
string, or null | yes | |
state |
string | 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. |