Upload File pdf API

Topic summary

Goal: upload a PDF to Shopify Files via API for use on product pages. Initial approach used REST POST to /admin/api/2023-10/files.json with multipart form data, resulting in 406 Not Acceptable; there is no REST /files endpoint.

Updated approach (confirmed working):

  • Use Admin GraphQL stagedUploadsCreate to generate a staged upload target with resource=FILE, filename, and mimeType=application/pdf.
  • Upload the file with a PUT request to stagedTargets.url returned in step 1. Send the raw file bytes to that URL; do not use multipart form data or extra parameters in headers/body.
  • On 200 OK from the upload, call the Admin GraphQL fileCreate mutation to add the file to the Files section, passing originalSource as the staged upload URL from step 1 without query parameters.

Clarifications: The upload step is PUT (not POST). The earlier confusion about passing parameters in body/headers does not apply for FILE; upload directly to the provided URL. A Postman example was referenced; documentation for FILE uploads is limited.

Status: Resolved with a working 3-step method above.

Summarized with AI on January 12. AI used: gpt-5.

Hi all. I’m using a Custom Data field to allow to upload a PDF file each product on their Shopify store. The file is going to be downloadable from each product page.

I would like to write a function in C# to upload files via the API but I can’t understand what the correct procedure is.

This is my code example (it doesn’t work):

        public async Task UploadFile(byte[] fileContent, string fileName)
        {
            using (var client = new HttpClient())
            {
                var requestUri = $"https://{_shopifyStoreUrl}/admin/api/2023-10/files.json";
                client.DefaultRequestHeaders.Add("X-Shopify-Access-Token", _accessToken);

                var content = new MultipartFormDataContent();
                var fileContentContent = new ByteArrayContent(fileContent);
                fileContentContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
                {
                    Name = "\"file\"",
                    FileName = $"\"{fileName}\""
                };
                content.Add(fileContentContent);

                var response = await client.PostAsync(requestUri, content);
                response.EnsureSuccessStatusCode();
            }
        }

Can you help me understand where I’m wrong?

The response is: “StatusCode: 406, ReasonPhrase: ‘Not Acceptable’”

Thanks a lot.

Hey @MarcoIngShop ,

I can’t speak to the C# code specifically but can point you in the right direction. I don’t think there’s a /files endpoint - but you should be able to create a file with this (GraphQL) mutation.

Thank you for your suggestion. I understood that the operation must be done in 2 steps.

The first is GenerateUploadUrl and then upload.

I think I managed to do the first step but then it fails when I send the file.

I have upload url and parameters.

Could you help me understand where the error is in the second step?

This is my code:

public async Task

Hey @MarcoIngShop

Are you able to capture the request ID from the respond headers? This will let me examine the logs.

X-GUploader-UploadID → ABPtcPqz1u45k2z5XfYw5nAdUtiIcSiOMp3mvHZ8spxt2_MB3PlIus6aMscZ73SIoVfc_tA1kxcXZCt7oQ

I thik that this is the point “critical”.

I would like to understand if I have to make a put or a post and if the parameters I send are correct (if they go in the header or in the body).

//Load File
            var formContent = new MultipartFormDataContent();
            foreach (var parameter in uploadParameters)
            {
                formContent.Add(new StringContent(parameter["value"]), parameter["name"]);
            }
            string base64String = Convert.ToBase64String(fileContent);
            formContent.Add(new StringContent(base64String),"data");
            formContent.Add(new StringContent(fileName), "filename");
            //formContent.Add(new ByteArrayContent(fileContent), "data", fileName);
            //formContent.Add(new ByteArrayContent(fileContent),"data");
            //HttpContent httpContent = new HttpContent();

                      
            var uploadResult = await _httpClient.PostAsync(uploadUrl, formContent);
            if (!uploadResult.IsSuccessStatusCode)
            {
                throw new Exception("Failed to upload file.");
            }

I can’t speak to the code directly, but this article breaks down the steps - maybe try running through this first: https://shopify.dev/docs/apps/online-store/media/products#generate-the-upload-url-and-parameters

It’s specifically for product media, so difference would be:

  • you would specify FILE for the StagedUploadTargetGenerateUploadResource

  • after uploading the file, you can add the file to the Files page in Shopify admin using the fileCreate mutation.

Hi,
This article is missing the example in “UPLOAD SECTION” of when a generic “FILE” type must be uploaded (in my case a PDF) so I can’t understand if the request must be of type POST or PUT and where (and how) the parameters should be passed.

Can you tell me if for a generic FILE (pdf) type the request must be POST OR PUT and how the parameters should be passed?

In the first step I followed the instructions by putting the type “FILE” and I received the correct parameters but when it says to upload I tried various ways without success.

Thanks a lot.

Can you check the log for this request:

X-GUploader-UploadID: ABPtcPqgQzIR9moKLvUInt0f35gTM7dK1NMGRDfIT2mEa1iEyUaGLDWVfniZHVTqTIeasppOz0s
Date: Thu, 23 Nov 2023 20:26:05 GMT
Server: UploadServer
Alt-Svc: h3=“:443”; ma=2592000
Alt-Svc: h3-29=“:443”; ma=2592000
Content-Type: text/plain; charset=utf-8
Content-Length: 25

Thanks a lot.

Hey @MarcoIngShop

Just tried it out - we could use better docs on the process :sweat_smile: I had success with the following method:

  1. Generate a staged upload URL
mutation generateStagedUploads {
  stagedUploadsCreate(input: [
    {
      filename: "example.pdf",
      mimeType: "application/pdf",
      resource: FILE
    }
  ])
    {
    stagedTargets {
      url
      resourceUrl
      parameters {
        name
        value
      }
    }
    userErrors {
      field, message
    }
  }
}
  1. Upload the file (A PUT request to data.stagedUploadsCreate.stagedTargets.url from step 1. If you want to test in Postman here’s an example screenshot.

  2. If you get a 200 OK from step 2, then you can use the fileCreate mutation to add the file to the ‘files’ section of the admin. You only need to pass the originalSource param, which is the data.stagedUploadsCreate.stagedTargets.url value from step one without any of the params.

{
  "files": [
    {
      "originalSource": "https://shopify-staged-uploads.storage.googleapis.com/tmp/.../example.pdf"
    }
  ]
}

Let me know how you go!