# Uploading assets

This API reference document contains information on uploading assets by file or remote URL

**Note:**

If you want to find out which asset system your project uses and which section of this document applies to you, [click here](https://hygraph.com/docs/api-reference/assets/assets-overview#which-asset-system-does-my-project-use).

Our asset system uses AWS S3's pre-signed upload URLs and includes the upload action into the GraphQL API.

This process will cover the following steps:

1. Send a `createAsset` mutation passing some basic information about the asset. This also works as a nested mutation.
2. You can [upload by file](https://hygraph.com/docs/api-reference/assets/uploading-assets#upload-by-file) or [by URL](https://hygraph.com/docs/api-reference/assets/uploading-assets#upload-by-remote-url). The upload works differently depending on your choice.
3. As soon as that the upload complete, the asset entry will switch from an internal  `ASSET_CREATE_PENDING` state to `ASSET_CREATE_COMPLETE`, and the asset will be served via its URL.

**Webhooks & asset uploads:**

Make sure you read our [webhooks documentation](https://hygraph.com/docs/api-reference/basics/webhooks#webhooks-and-assets) to understand how they work with the Hygraph Asset Management System.

### Upload by file

This is a two-step process:

1. Create the asset via GraphQL mutation. This returns a URL that needs to be called to execute the upload.
3. Use that pre-signed URL to upload the asset file. The asset stays pending until the file gets successfully uploaded and analysed.

**Note:**
Size limits for uploaded files depend on the plan. [Check out our pricing page.](https://hygraph.com/pricing)

#### Create asset via GraphQL mutation

The first step is to create the actual asset.

**Pro Tip:**
If you don't pass a `fileName`, the system uses the file name of the file you provide - via URL or local file.

If you pass a `fileName`, it overwrites the name of the file you provide. You will need to add it to `data`.

Here's a sample mutation to create an asset called `test.jpg`, and the JSON response:

  **Mutation**

```graphql
mutation createAsset {
  createAsset(data: {}) {
    id
    url
    upload {
      status
      expiresAt
      error {
        code
        message
      }
      requestPostData {
        url
        date
        key
        signature
        algorithm
        policy
        credential
        securityToken
      }
    }
  }
}
```

  
  **Response**

```json
{
  "data": {
    "createAsset": {
      "id": "clt47n0t600j807vvirlzi1xx",
      "url": "https://eu-central-1.graphassets.com/clpqzrnm4007e01t810b59ir4/clt47n0t600j907vveibipmov",
      "upload": {
        "status": "ASSET_CREATE_PENDING",
        "expiresAt": "2024-02-27T12:38:49+00:00",
        "error": null,
        "requestPostData": {
          "url": "https://eu-1-assets-delivery-hg75hf.s3.eu-central-1.amazonaws.com",
          "date": "20240227T101349Z",
          "key": "clpqzrnm4007e01t810b59ir4/upload/clpqzrnh4001q01t84tl21zdl/clpqzrnh6001u01t866zfftoa/clt47n0t600j907vveibipmov/${filename}",
          "signature": "c17e7b1c5d4af665a8fc74421fae53b72e94bb19e85e7befd1eb79b865bef7d2",
          "algorithm": "AWS4-HMAC-SHA256",
          "policy": "eyJleHBpcmF0aW9uIjoiMjAyNC0wMi0yN1QxMjozODo0OS45MTRaIiwiY29uZGl0aW9ucyI6W3siYnVja2V0IjoiZGV2LTEtYXNzZXRzLWRlbGl2ZXJ5LWY3OGM1YjUifSx7ImtleSI8ImNscHF6cm5tNDAwN2UwMXQ4MTBiNTlpcjQvdXBsb2FkL2NscHF6cm5oNDAwMXEwMXQ4NHRsMjF6ZGwvY2xwcXpybmg2MDAxdTAxdDg2NnpmZnRvYS9jbHQ0N24wdDYwMGo5MDd2dmVpYmlwbW92LyR7ZmlsZW5hbWV9In0seyJ7LWFtei1hbGdvcml0aG0iOiJBV1M0LUhNQUMtU0hBMjU2In0seyJ4LWFtei1jcmVkZW50aWFsIjoiQVNJQVZRUkUzVk1FV0dZNUMyWEwvMjAyNDAyMjcvZXUtY2VudHJhbC0xL3MzL2F3czRfcmVxdWVzdCJ9LHsieC1hbXotZGF0ZSI6IjIwMjQwMjI3VDEwMTM0OVoifSx7IngtYW16LXNlY3VyaXR5LXRva2VuIjoiSVFvSmIzSnBaMmx1WDJWakVPci8vLy8vLy8vLy93RWFER1YxTFdObGJuUnlZV3d0TVNKSE1FVUNJRVppa0hoNmIrM1lZWWJyU2xFaWtpZWwvVnZZdCs0SEpWTE5QcFFzZ0tibkFpRUFnUldLdURxR3AzRUJic3A2MTRFUTZwMGs2RnoweS9mdDhLRk1USTltUmVzcS9RTUkwLy8vLy8vLy8vLy9BUkFGR2dadd3ek56a3hNRGd4TnpRMk1ERWlERTJPSWF5RDNEQXI3cE9OSVNyUkExYk1haXdON1FZSW5SSjNuby8vY0F3cm0rbjg0SVZOdWsraER3QXR2SG54aWxDcCtNZktlMVh6MTN1TWhrTWNGa1c2T2NGZXF1R09lUHJYV28yNjdEaXlKRkE0V0dqU3dxem8xVFRWUjhJTkZoaUJlWmdxa2Q4bEtZQnVRYzBsbWxIekhLamk1WVloYStVZG45L1BrRit3dlp5ZFF6Qk53NzJzakEwT2QrakxaUEhuR29kK2Z3WjFUT2o5RE4zd3NQb2E0MTU3OFBvU1ZSRzB1ZGN4RGFHejVxeFh6OXZvb2cwQ3pTVlFiUk1tNW1UTHdSelp3ZlJYMTQ0U2NxMUsrYVNMVE9UdW1mYzROTjFzdlVEODZ0VU51azFGVUxzOE5FZi9PcFlhZmcwMkhyWkEvRHlDSnhlS3A1SkRxRndCMGJZL2lUcTRqS1VLOFBDQXF1T3Q2bFRQRWRTTFJhTnVBSW43cWd6UFNmRi8rQ3JjREtEblZTWmlNR3VhSlA1cmhjUlRob01XdHFjKzdiL3pPNjhMNVdrL0o5SGtmZzNjNmh5UGlsTkRhMG1RY0lhU0VzT0dFTkhQL2Q5K2xJSUhJZWJXNDFmT3VMSVltWEJlaWVGTzFYNDRmbk5Ld0F2bDlkNkxmQTZxdiswNlJtSFdRYkg5VjdLNmRiOHp4ZDNYMDFXRldTckVKdEFEU2xRNkdvejdNRmtIaVk2SnF2NEpjWnJlV1pVTXhFSTgydEVRWWR6R3lwdmh1ZEd6TDhJR3k4QmpjcnFQakhMWjRRUTRMaW9hcS95czNrM1BSZ1QwcEtIT0RMNm5jK1E2cmpDUTR2YXVCanFsQWQ2VmxidGxvYXpFWlc1SUhqRGFVYTk3VjFwV3UxamxVTlN0dDNLNVZmeDJRWlNtdXdUdDl3bitQYzJZL0xpMDExU0pSYWZCZy9oQlhmMWpDTG4yZ2lLMkFuMEovWGxnZ3FQSmdUYnhydS83RThsTFdmOG9uL1g4aVc1V1AxNDZOMjVaZk0xdXh0RGNQNTB5eWhGemFaOGRjR1p3c1FFRjY3b01mNSsxN3JiVC9kT2k3WkE1RTFLVG4vbmJMZUVhcEhZckQ5SkJESUtTZjRyaWhyLzkzcUtYTjFWajh3PT0ifV19",
          "credential": "AFDGAVRRE3VMFWGY5C2XL/20240227/eu-central-1/s3/aws4_request",
          "securityToken": "IQoJb3JpZ2luX2VjEOr//////////wEaDGV1LWNlbnRyYWwtMSJHMEUCIEZikHh6b+3YYYbrSlEikiel/VvYt+4HJVLNPpQsgKbnAiEAgRWKuDqGp3EBbsp614EQ6p0k6Fz0y/ft8KFMTI9mResq/QMI0///////////ARAFGgwzNzkxMDgxNzQ2MDEiDE2OIayD3DAr7pONISrRA1bMaiwN7QYInRJ3no//cAwrm+n84IVNuk+hDwAtvHnxilCp+MfKe1Xz13uFhiBeZgqkd8lKYBuQc0lmlHzHKji5n9/PkF+wvZydQzBNw72sjA0Od+jLZPHnGod+fwZ1TOj9DN3wsPoa41578PoSVRG0udcxDaGz5qxXz9voog0CzSVQbRMm5mTLwRzZwfRX144Scq1K+aSLTOTumfc4NN1svUD86tUNuk1FULs8NEf/OpYafg02HrZA/DyCJxesdgsdgerKp5JDqFwB0bY/iTq4jKUK8PCAquOt6lTPEdSLRaNuAIn7qgzPSfF/+CrcDKDnVSZiMGuaJP5rhcRThoMWtqc+7b/zO68L5Wk/J9Hkfg3c6hyPilNDa0mQcIaSEsOGENHP/d9+lIIHIebW41fOuLIYmXBeieFO1X44fnNKwAvl9d6LfA6qv+06RmHWQbH9V7K6db8zxd3X01WFWSrEJtADSlQ6y8BjcrqPjHLZ4QQ4Lioaq/ys3k3PRgT0pKHODL6nc+Q6rjCQ4vauBjqlAd6VlbtloazEZW5IHjDaUa97V1pWu1jlUNStt3K5Vfx2QZSmuwTt9wn+Pc2Y/Li011SJRafBg/hBXf1jCLn2giK2An0J/XlggqPJgTbxru/7E8lLWf8on/X8iW5WP146N25ZfM1uxtDcP50yyhFzaZ8dcGZwsQEF67oMf5+17rbT/dOi7ZA5E1KTn/nbLeEapHYrD9JBDIKSf4rihr/93qKXN1Vj8w=="
        }
      }
    }
  }
}
```


**Warning:**
Regarding the expiry of the `requestPostData` information:

You can use the `expiresAt` field for this.

If you use the `requestPost` info after that date, it will fail.

#### Upload asset

We will use curl for this example.

Imagine we extracted the `data.createAsset.upload.requestPostData` subkeys into variables, and we have a file `test.jpg` in our current working directory:

```bash
URL="https://dev-1-assets-delivery-f78c5b5.s3.eu-central-1.amazonaws.com"
DATE="20240123T173451Z"
KEY="clr7o58jb00w701vwhpc69xda/upload/clr7o587k000j01uhul69p0rb/clr7o58ca00qi01vw7wzoguau/clrqmzdpn00id0bvucez7gnm9"
SIGNATURE="3f812cc0b05d59ee3d1efdd9dc046ed6edf595749c2763b85b923133a84e8d86"
ALGORITHM="AWS4-HMAC-SHA256"
POLICY="eyJleHBpcmF0aW9uIjoiMjAyNC0wMS0yM1QxOTo1OTo1MS45NDdaIiwiY29uZGl0aW9ucyI6W3siYnVja2V0IjoiZGV2LTEtYXNzZXRzLWRlbGl2ZXJ5LWY3OGM1YjUifSx7ImtleSI6ImNscjdvNThqYjAwdzcwMXZ3aHBjNjl4ZGEvdXBsb2FkL2NscjdvNTg3azAwMGowMXVodWw2OXAwcmIvY2xyN281OGNhMDBxaTAxdnc3d3pvZ3VhdS9jbHJxbXpkcG4wMGlkMGJ2dWNlejdnbm05In0seyJ4LWFtei1hbGdvcml0aG0iOiJBV1M0LUhNQUMtU0hBMjU2In0seyJ4LWFtei1jcmVkZW50aWFsIjoiQVNJQVZRUkUzVk1FMkJHWVZLT1AvMjAyNDAxMjMvZXUtY2VudHJhbC0xL3MzL2F3czRfcmVxdWVzdCJ9LHsieC1hbXotZGF0ZSI6IjIwMjQwMTIzVDE3MzQ1MVoifSx7IngtYW16LXNlY3VyaXR5LXRva2VuIjoiSVFvSmIzSnBaMmx1WDJWakVLci8vLy8vLy8vLy93RWFER1YxTFdObGJuUnlZV3d0TVNKSE1FVUNJRUF6SFQ2OUdEWm9CcXp5Nkw4bExDUDFEZnNBZ3FaM0phRms0M3ZvcGNGRUFpRUFrVTljUVNTSTFCRmV5TVZjTEI1QkFrVzg3NUE2M2hkWTVnNDgxcEY4QitNcTlBTUlZeEFGR2d3ek56a3hNRGd4TnpRMk1ERWlERkxzOHB2a0JHRU9aV3dSZVNyUkF4amJvVXRobDRtRUM3VHVzTHpFcXc1T1JUOUZRSStYYVVQWXZRZi9ST3ZNOExBVkFYVHQwMWZHT0JyUkVRSDRDckJLSVVtMDNtQWFPd05nQ2g1aGFlTDhmTzZvRUJ3aTFLa2ZuRWpNWmtQcVJma0krMDZhK0o0SVBYTmR0bEt6b0VVOVd4Uk9TMzEyTC8rMjBpUkpLNmNoWmhhK04zVXNJMzRYR0U0L2IzNzV0aXpsWUx0RHJYN0JiMXFIdFVNYlg3VWVUaVJaSDU4S1VwRjloK2QyWWk2bUhuL1lGcmJVaGpOcjVGL1pkL2FkdU5nOGpKU3liVkw5bTVZdWQvdlI3enMyNXdpYlpJK3BOQjFXcVZzRWtiN09EN2g1QjFlQkE5dnNMdzBKdVlFMmFnYm0xNjF0SjZVS3A5Tkd4YmVWaFM1TjQ5N2txV2MzN3pCT3ZxdWxUU1JHUW04V1QrNGx1YzZyd2VTT0lFeUxMaFowT1ZCSWM1Q0Nrc1YvaFAxT2dxeUdSNGdKTDlwa3RhUDl0encxaFhyUHJhUW1mRHhoaVgrUStaNitkSWFxalY2VGlwcVhqTk9jM2tFSFIwNmpydnlhOVFsRGxXaWd2eGRaTHkwZktzQW8vR0dBTlhlM2ZGSzhVclVObE4ySHFGeHpBYU9JNEJPMmU1VE1sUzA5MTZZTFZUN1Zxd3hHOHQ0NTlUTUFLcXpJMTJ4N2JTbHFYQ0lHUlc1anlsazhaTE1mbGRZS2t6eDN6dWNKZDYvYVN4Q1c1bE1pa2lGRjYrbW9WQW9kck1qeGhLZW42QnNRZ3ZMVW1kamFmaVROMkRERDhMK3RCanFsQVRzWGdYUmdZOUVJQzJMUXEyOU0xSGRlbmViY3I5YU5JbmFBTVFKRlpMUnl1bnM3dzk0MnhjaW1sSTYyak15RWFUczRZM3M3aTMwYVpqc1E2ampMK1hKMVJTQW5Ta2lndFJZbHVKSGFlaWJkRDMydEZDamZhY3ovZWF5eVhGcEgzekc5NHJRZUk1V0ZBWVZXRFZyWEN2WmJROG9SejV4OHN5ZFBBZWROR1VEOHYrMk5XWDc0NTBTcXdXc2Jta0dlekxoZ0pzNkxyOVhSSG5rTEljdGFoUDVoazg0Q3p3PT0ifV19"
CREDENTIAL="ASIAVQRE3VME2BGYVKOP/20240123/eu-central-1/s3/aws4_request"
SECURITY_TOKEN="IQoJb3JpZ2luX2VjEKr//////////wEaDGV1LWNlbnRyYWwtMSJHMEUCIEAzHT69GDZoBqzy6L8lLCP1DfsAgqZ3JaFk43vopcFEAiEAkU9cQSSI1BFeyMVcLB5BAkW875A63hdY5g481pF8B+Mq9AMIYxAFGgwzNzkxMDgxNzQ2MDEiDFLs8pvkBGEOZWwReSrRAxjboUthl4mEC7TusLzEqw5ORT9FQI+XaUPYvQf/ROvM8LAVAXTt01fGOBrREQH4CrBKIUm03mAaOwNgCh5haeL8fO6oEBwi1KkfnEjMZkPqRfkI+06a+J4IPXNdtlKzoEU9WxROS312L/+20iRJK6chZha+N3UsI34XGE4/b375tizlYLtDrX7Bb1qHtUMbX7UeTiRZH58KUpF9h+d2Yi6mHn/YFrbUhjNr5F/Zd/aduNg8jJSybVL9m5Yud/vR7zs25wibZI+pNB1WqVsEkb7OD7h5B1eBA9vsLw0JuYE2agbm161tJ6UKp9NGxbeVhS5N497kqWc37zBOvqulTSRGQm8WT+4luc6rweSOIEyLLhZ0OVBIc5CCksV/hP1OgqyGR4gJL9pktaP9tzw1hXrPraQmfDxhiX+Q+Z6+dIaqjV6TipqXjNOc3kEHR06jrvya9QlDlWigvxdZLy0fKsAo/GGANXe3fFK8UrUNlN2HqFxzAaOI4BO2e5TMlS0916YLVT7VqwxG8t459TMAKqzI12x7bSlqXCIGRW5jylk8ZLMfldYKkzx3zucJd6/aSxCW5lMikiFF6+moVAodrMjxhKen6BsQgvLUmdjafiTN2DDD8L+tBjqlATsXgXRgY9EIC2LQq29M1Hdenebcr9aNInaAMQJFZLRyuns7w942xcimlI62jMyEaTs4Y3s7i30aZjsQ6jjL+XJ1RSAnSkigtRYluJHaeibdD32tFCjfacz/eayyXFpH3zG94rQeI5WFAYVWDVrXCvZbQ8oRz5x8sydPAedNGUD8v+2NWX7450SqwWsbmkGezLhgJs6Lr9XRHnkLIctahP5hk84Czw=="

curl --request POST \
  --url $URL
  --form X-Amz-Date=$DATE
  --form key=$KEY
  --form X-Amz-Signature=$SIGNATURE
  --form X-Amz-Algorithm=$ALGORITHM
  --form policy=$POLICY
  --form X-Amz-Credential=$CREDENTIAL
  --form X-Amz-Security-Token=$SECURITY_TOKEN
  --form file=@./test.jpg
```

**Warning:**
You must put the `file` as last form entry. All other form entries must come before it.

The system also allows you to reupload a file for an existing asset entry. [Click here to know more.](https://hygraph.com/docs/api-reference/assets/updating-assets#update-existing-asset)

### Upload by remote URL

You can upload assets by remote URL in the GraphQL API by passing the URL of an asset hosted somewhere publicly accessible in the `createAsset` mutation.

```graphql
mutation test {
  createAsset(
    data: {
			uploadUrl:"https://images.unsplash.com/photo-1682687218147-9806132dc697"
    }
  ) {
    id
    url
  }
}
```

**Warning:**
As this is an asynchronous process, the image may still become `PENDING` until fully uploaded.

The system also allows you to reupload a file for an existing asset entry. [Click here to know more.](https://hygraph.com/docs/api-reference/assets/updating-assets#update-existing-asset)

## Legacy asset system

Hygraph supports uploading assets via HTTP. You'll need a [Permanent Auth Token](https://hygraph.com/docs/api-reference/basics/authorization#permanent-auth-tokens) with **Mutations** API access enabled to upload by file, or URL.

Assets are treated just like any other content entry, so they are automatically bound to the [environment](https://hygraph.com/docs/api-reference/basics/environments), and [authorization](https://hygraph.com/docs/api-reference/basics/authorization) settings of your project.

**Warning:**
- You must append `/upload` to your project API endpoint when uploading assets. For example, `https://[region].hygraph.com/v2/[projectId]/[environment]/upload`.
- You can upload assets to your project without the need of a [Permanent Auth Token](https://hygraph.com/docs/api-reference/basics/authorization#permanent-auth-tokens) by adding Read and Create permissions to the Content API. **This is however unsafe and not advised**, as exposing the endpoint anywhere - like your website - while having any write access on the Public API, would essentially allow anyone to modify your data.

### Upload by file (Legacy)

**Note:**
Size limits for uploaded files depend on the plan. [Check out our pricing page.](https://hygraph.com/pricing)

  **cURL**

```bash
curl -XPOST -H "Authorization: Bearer {YOUR_PAT_VALUE}" -F fileUpload=@picture.jpg https://[region].hygraph.com/v2/[projectId]/[environment]/upload
```

  
  **Node.js**

```js
// Your file must have the .mjs extension because node-fetch is an ESM-only package.
import fetch, { FormData, fileFrom } from 'node-fetch';

const form = new FormData();

form.set('fileUpload', await fileFrom('path/to/file.png'));

fetch(`${process.env.HYGRAPH_URL}/upload`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.HYGRAPH_ASSET_TOKEN}`,
  },
  body: form,
})
  .then((res) => res.json())
  .then((data) => console.log(JSON.stringify(data, null, 2)))
  .catch((err) => console.log(err));
```

  
  **Response**

```json
{
  "filename": "pexels-photo-1170986.jpeg",
  "mimetype": "image/jpeg",
  "size": 32476,
  "width": 500,
  "height": 750,
  "url": "https://media.graphassets.com/P3TkBzxyQLupgDWNFydB",
  "id": "ckfdz530o0001ip92cdr3bbmj"
}
```


Here's also a standard JavaScript example:

```js
const HYGRAPH_URL = '';
const HYGRAPH_ASSET_TOKEN = '';

async function upload() {
  const input = document.getElementById('fileUpload');
  const file = input.files[0];

  const form = new FormData();

  form.append('fileUpload', file);

  // It is not recommended to use the HYGRAPH_ASSET_TOKEN in the Front-End.
  // In this example we're using it, but in a real application you should
  // use a backend to upload the file and use the HYGRAPH_ASSET_TOKEN there.
  const response = await fetch(`${HYGRAPH_URL}/upload`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${HYGRAPH_ASSET_TOKEN}`,
    },
    body: form,
  });

  const data = await response.json();
  console.log(JSON.stringify(data, null, 2));
}
```

### Upload by remote URL (Legacy)

You can also upload files by providing a remote URL, instead of a file.

  **cURL**

```bash
curl -XPOST -d url=https://media.graphassets.com/P3TkBzxyQLupgDWNFydB https://[region].hygraph.com/v2/[projectId]/[environment]/upload
```

  
  **Node.js**

```js
// Your file need to have the .mjs extension because node-fetch is an ESM-only package.
import fetch from 'node-fetch';

fetch(`${process.env.HYGRAPH_URL}/upload`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.HYGRAPH_ASSET_TOKEN}`,
    'Content-Type': 'application/x-www-form-urlencoded',
  },
  body: `url=${encodeURIComponent(
    'https://media.graphassets.com/P3TkBzxyQLupgDWNFydB'
  )}`,
})
  .then((res) => res.json())
  .then((data) => console.log(JSON.stringify(data, null, 2)))
  .catch((err) => console.log(err));
```

  
  **Response**

```json
{
  "filename": "pexels-photo-1170986.jpeg",
  "mimetype": "image/jpeg",
  "size": 32476,
  "width": 500,
  "height": 750,
  "url": "https://media.graphassets.com/J9BOEF3OSuuSXDhvriQa",
  "id": "ckgs20b80017109547vfev24c"
}
```
