Search code examples
javascriptnode.jsaxioswhatsappform-data

Whatsapp Cloud API uploading media files error


currently I am trying to make a thin wrapper around Whatsapp Cloud API. One of the problem I am currently facing is that the uploading media files isn't working.

Looking through the documentation for uploading files, it seems that it expects a multipart/form-data payload to the endpoint.

Here is my current implementation

import FormData from 'form-data';
import fs from 'fs';
import axios from 'axios';

const formData = new FormData();

formData.append('file', fs.createReadStream('path/to/my/file.jpg'));
formData.append('messaging_product', 'whatsapp');
formData.append('type', 'image/jpeg');

await axios.post('https://graph.facebook.com/v14.0/PHONE_NO_ID/media', formData, {
  headers: { 'Authorization': ACCESS_TOKEN }
});

Looking at the error that it returns, it seems that I am somehow missing the messaging_product field even though I have properly add it in the formData.

{
  "error": {
    "message": "(#100) The parameter messaging_product is required.",
    "type": "OAuthException",
    "code": 100,
    "fbtrace_id": "FBTRACE_ID"
  }
}

The Postman collection works for uploading media file, so I am thinking that the file field of the formData is the problem. Is fs.createReadStream on a file equivalent to how Postman handle file uploading

EDIT: The question has been solved, the problems was not adding in the headers generated by the formData, Thanks Phil below!


Solution

  • Love the documentation... they say type is a required parameter then go on to show a curl example without it 🤦.

    Also, file is not the...

    Path to the file stored in your local directory

    but instead a mime-encoded binary.

    In any case, the type needs to be part of the mime-encoding by way of the content-type meta data and you then need to forward the content-type header generated by FormData.

    You also may be missing the Bearer authentication token scheme.

    const PHONE_NO_ID = "something";
    const ACCESS_TOKEN = "something";
    
    const formData = new FormData();
    
    formData.append("file", fs.createReadStream("path/to/my/file.jpg"), {
      contentType: "image/jpeg",
    });
    formData.append("messaging_product", "whatsapp");
    
    try {
      const {
        data: { id: mediaId },
      } = await axios.post(
        `https://graph.facebook.com/v14.0/${PHONE_NO_ID}/media`,
        formData,
        {
          headers: {
            Authorization: `Bearer ${ACCESS_TOKEN}`, // "Bearer" prefix
            ...formData.getHeaders(), // merge in FormData headers
          },
        }
      );
      console.log("media ID", mediaId);
    } catch (err) {
      console.error("upload", err.toJSON());
    }