Sorry, no results found for "".

Show examples in:
Javascript HTTP

Content Management API > Upload

Create a new upload

⚠️ We highly advocate for utilizing our JavaScript client when uploading new assets, as it comes equipped with high-level helper methods that handle all the nitty-gritty for you.

Uploading a new asset to a DatoCMS project manually is a process that necessitates several HTTP calls. This approach significantly accelerates the upload process for DatoCMS, as it eliminates the need for us to act as an intermediary between you, the user aiming to upload an asset, and the storage bucket.

First, send a POST request to the /upload-requests endpoint to obtain the location on which to perform the physical file upload:

POST https://site-api.datocms.com/upload-requests HTTP/1.1
Authorization: Bearer YOUR-API-TOKEN
Accept: application/json
X-Api-Version: 3
Content-Type: application/vnd.api+json
{
"data": {
"type": "upload_request",
"attributes": {
"filename": "image.png"
}
}
}
Terminal window
curl -g 'https://site-api.datocms.com/upload-requests' \
-X POST \
-H "Authorization: Bearer YOUR-API-TOKEN" \
-H "Accept: application/json" \
-H "X-Api-Version: 3" \
-H "Content-Type: application/vnd.api+json" \
--data-binary '{"data":{"type":"upload_request","attributes":{"filename":"image.png"}}}'
await fetch("https://site-api.datocms.com/upload-requests", {
method: "POST",
headers: {
Authorization: "Bearer YOUR-API-TOKEN",
Accept: "application/json",
"X-Api-Version": "3",
"Content-Type": "application/vnd.api+json",
},
body: JSON.stringify({
data: { type: "upload_request", attributes: { filename: "image.png" } },
}),
});
HTTP/1.1 202 Accepted
Content-Type: application/json
Cache-Control: cache-control: max-age=0, private, must-revalidate
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 28
{
"data": {
"type": "upload_request",
"id": "/7/1455102967-image.png",
"attributes": {
"url": "https://dato-images.s3.eu-west-1.amazonaws.com/205/1565776891-image.png?x-amz-acl=public-read&...",
"request_headers": {
"X-Foo": "bar"
}
}
}
}

Now it is possible to use the obtained information to build an HTTP request that effectively uploads the file:

  • The HTTP method MUST be PUT
  • The URL is the one specified in the url attribute of the previous response
  • Each header specified in the request_headers attribute of the previous response MUST be set as a request header
  • It is possible to add a Content-Type header to specify the type of file being uploaded
  • The content of the file must be inserted as the raw body of the request (no multipart upload)
PUT https://dato-images.s3.eu-west-1.amazonaws.com/205/1565776891-image.png?x-amz-acl=public-read&... HTTP/1.1
Content-Type: image/png
X-Foo: bar
<YOUR_FILE_BINARY_CONTENT>
Terminal window
curl -g 'https://dato-images.s3.eu-west-1.amazonaws.com/205/1565776891-image.png?x-amz-acl=public-read&...' \
-X PUT \
-H "Content-Type: image/png" \
-H "X-Foo: bar" \
--data-binary '<YOUR_FILE_BINARY_CONTENT>'
await fetch(
"https://dato-images.s3.eu-west-1.amazonaws.com/205/1565776891-image.png?x-amz-acl=public-read&...",
{
method: "PUT",
headers: { "Content-Type": "image/png", "X-Foo": "bar" },
body: "<YOUR_FILE_BINARY_CONTENT>",
},
);
HTTP/1.1 202 Accepted
X-Amz-Request-Rd: XXX
Server: AmazonS3
{
"data": {
"type": "job",
"id": "4235"
}
}

This step initiates a long-running background job to associate your S3 upload to the correct asset in DatoCMS. It normally takes 3-5 seconds, but can sometimes take a bit longer under heavy load. Once the background job is completed, the asset will appear in your DatoCMS media area.

To start the job, make a POST request to the /uploads endpoint with the path being the ID of the request made on step 1. You'll get back a job ID, which you can separately query in the next step in order to check on the background job status.

Notes:

  • If you choose to provide default field metadata, the alt, title, and custom_data fields are required.

  • Also, please note that validation errors will not be immediately returned in this step. You must use the next step (retrieve the job result) to see any errors that might've occurred.

  • If you are uploading multiple files and trying to optimize upload performance, it is safe to start another upload as soon as this step is done (i.e., you got back a 202 indicating the background job has started). However, you should still check the background job status for each file a few seconds later to be sure each one completed without error. See next step for details.

POST https://site-api.datocms.com/uploads HTTP/1.1
Authorization: Bearer YOUR-API-TOKEN
Accept: application/json
X-Api-Version: 3
Content-Type: application/vnd.api+json
{
"data": {
"type": "upload",
"attributes": {
"path": "/205/1565776891-image.png",
"author": "Mark",
"copyright": "2020 DatoCMS",
"default_field_metadata": {
// Alt, title, and custom_data are required with default metadata
"en": {
"alt": "Nyan the cat",
"title": "My cat",
"custom_data": {}
}
}
}
}
}
Terminal window
curl -g 'https://site-api.datocms.com/uploads' \
-X POST \
-H "Authorization: Bearer YOUR-API-TOKEN" \
-H "Accept: application/json" \
-H "X-Api-Version: 3" \
-H "Content-Type: application/vnd.api+json" \
--data-binary '{
"data": {
"type": "upload",
"attributes": {
"path": "/205/1565776891-image.png",
"author": "Mark",
"copyright": "2020 DatoCMS",
"default_field_metadata": {
// Alt, title, and custom_data are required with default metadata
"en": {
"alt": "Nyan the cat",
"title": "My cat",
"custom_data": {}
}
}
}
}
}
'
await fetch("https://site-api.datocms.com/uploads", {
method: "POST",
headers: {
Authorization: "Bearer YOUR-API-TOKEN",
Accept: "application/json",
"X-Api-Version": "3",
"Content-Type": "application/vnd.api+json",
},
body: '{\n "data": {\n "type": "upload",\n "attributes": {\n "path": "/205/1565776891-image.png",\n "author": "Mark",\n "copyright": "2020 DatoCMS",\n "default_field_metadata": {\n // Alt, title, and custom_data are required with default metadata\n "en": {\n "alt": "Nyan the cat",\n "title": "My cat",\n "custom_data": {}\n }\n }\n }\n }\n}\n',
});
HTTP/1.1 202 Accepted
Content-Type: application/json
Cache-Control: cache-control: max-age=0, private, must-revalidate
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 28
{
"data": {
"type": "job",
"id": "facf9248be977002c9bae231"
}
}

This is the final step in the upload creation lifecycle.

Whereas the previous step started the background job, this endpoint tells you the real-time status of that job. This is how you verify the success or failure of an upload.

Although technically an optional step, we still recommend querying this endpoint a few times in order to keep track of the upload lifecycle. You should keep polling until it either completely succeeds or fails early.

This endpoint tells you several useful things:

  • While the job is still processing, the endpoint will return a 404 status code. Keep querying once every second or two until you either get a success or failure state.
  • If the job finishes successfully (i.e., the S3 upload was successfully associated with a DatoCMS asset), you will get back a 200 status code and the successfully associated upload object in the payload. At this point, the asset will be available in the DatoCMS media area.
  • If the job fails due to a validation or other error, you will immediately get back a 422 or similar error code. Check payload.data[] for any objects of type: "api_error", and its attributes.details.code and attributes.details.message will tell you specifically what failed.
  • Once the job either succeeds or fails, the job result will persist for approximately 15 minutes. After that, it will 404 again.

On validation failure

This is an example 422 validation failure response:

{
"data": {
"id": "c628b8ccf485a127775c635a",
"type": "job_result",
"attributes": {
"payload": {
"data": [
{
"id": "6a1131",
"type": "api_error",
"attributes": {
"code": "INVALID_FIELD",
"details": {
"field": "default_field_metadata.en",
"code": "INVALID_FORMAT",
"message": "Must contain alt, title and custom_data"
},
"doc_url": "https://www.datocms.com/docs/content-management-api/errors#INVALID_FIELD"
}
}
]
},
"status": 422,
"statusText": "Unprocessable Entity"
}
}
}

Validation errors should be available within a second or two under typical conditions. If you don't get an error, it's safe to assume the job is still processing... just keep polling and you should get a success soon afterward. The whole process usually takes only 3-5 seconds per image, but can be a little slower under heavy load.

On success

A successful response would look like the "HTTP Response" below.

GET https://site-api.datocms.com/job-results/facf9248be977002c9bae231 HTTP/1.1
Authorization: Bearer YOUR-API-TOKEN
Accept: application/json
X-Api-Version: 3
Content-Type: application/vnd.api+json
Terminal window
curl -g 'https://site-api.datocms.com/job-results/facf9248be977002c9bae231' \
\
-H "Authorization: Bearer YOUR-API-TOKEN" \
-H "Accept: application/json" \
-H "X-Api-Version: 3" \
-H "Content-Type: application/vnd.api+json"
await fetch(
"https://site-api.datocms.com/job-results/facf9248be977002c9bae231",
{
headers: {
Authorization: "Bearer YOUR-API-TOKEN",
Accept: "application/json",
"X-Api-Version": "3",
"Content-Type": "application/vnd.api+json",
},
},
);
HTTP/1.1 202 Accepted
Content-Type: application/json
Cache-Control: cache-control: max-age=0, private, must-revalidate
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 28
{
"data": {
"type": "job-result",
"id": "facf9248be977002c9bae231",
"attributes": {
"status": 200,
"payload": {
"data": {
"type": "upload",
"id": "666",
"attributes": {
"size": 444,
"width": 30,
"height": 30,
"path": "/205/1565776891-image.png",
"url": "https://www.datocms-assets.com/205/1565776891-image.png",
"basename": "image",
"format": "jpg",
"alt": "Nyan the cat",
"title": "My cat",
"is_image": true,
"author": "Mark",
"copyright": "2020 DatoCMS",
"default_field_metadata": {
"en": {
"alt": "Nyan the cat",
"title": "My cat",
"custom_data": {}
}
}
}
}
}
}
}
}

Body parameters

id string Optional

RFC 4122 UUID of upload expressed in URL-safe base64 format

Example: "q0VNpiNQSkG6z0lif_O1zg"
type string Required

Must be exactly "upload".

attributes.path string Required

Upload path

Example: "/45/1496845848-digital-cats.jpg"
attributes.copyright string, null Optional

Copyright

Example: "2020 DatoCMS"
attributes.author string, null Optional

Author

Example: "Mark Smith"
attributes.notes string, null Optional

Notes

Example: "Nyan the cat"
attributes.default_field_metadata object Optional

For each of the project's locales, the default metadata to apply if nothing is specified at record's level.

Example: { en: { title: "this is the default title", alt: "this is the default alternate text", custom_data: { foo: "bar" }, focal_point: { x: 0.5, y: 0.5 }, }, }
attributes.tags Optional

Tags

Type: Array<string>
Example: ["cats"]
relationships.upload_collection.data Optional

Upload collection to which the asset belongs

Returns

Returns a Job ID. You can then poll for the completion of the job that will eventually return a resource object of type upload