Search code examples
axiosrequestgmailgmail-api

A 'Missing draft message' 401 error while trying to create a draft


While trying to POST at https://gmail.googleapis.com/gmail/v1/users/${user_id}/drafts, in order to create a gmail draft, I get prompt with a Missing draft message error.

This is the actual code that make the request:

let userMail = axios.post(`https://gmail.googleapis.com/gmail/v1/users/${user_id}/drafts`,
  {
    body: {
      draft: {
        message: {
          raw: "Hard Coded mail",
        }
      }
    }
  },
  {
    headers: {
      Authorization: `Bearer ${access_token}`,
    }
  })
  console.log(userMail)

The console.log(userMail) show's a bunch of information such as:

data: {
      error: {
        code: 400,
        message: 'Missing draft message',
        errors: [
          {
            message: 'Missing draft message',
            domain: 'global',
            reason: 'invalidArgument'
          }
        ],
        status: 'INVALID_ARGUMENT'
      }
}

Am I missing on something in the body of the request, or is the syntax incorrect ?

According to this thread Missing draft message - javascript Gmail API - how to structure body of the request?,

"The correct structure of the request:"

'draft': {
  'message': {
    'raw': base64EncodedEmail
  }
}

PS: I'm not using external node modules, keeping it "vanilla"

EDIT (1): Trying to add From, To and Subject fields:

var message = 'MIME-Version: 1.0\r\n' +
  'Content-type: multipart/alternative; boundary=boundaryboundary\r\n\r\n' +
  'From: ' + from + "\r\n" +
  'To: ' + to + "\r\n" +
  'Subject: ' + subject + "\r\n" +
  '--boundaryboundary\r\n' +
  'Content-type: text/plain; charset=UTF-8\r\n' +
  mailContent + "\r\n\r\n" +
  '--boundaryboundary--';

Solution

  • Modification points:

    • In your situation, the request body is JSON.stringify({"message": {"raw": data}}).

    • Please include "Content-Type": "application/json" in the request header.

    • When you want to put a text of Hard Coded mail, please create the request body as follows.

        MIME-Version: 1.0
        Content-type: multipart/alternative; boundary=boundaryboundary
      
        --boundaryboundary
        Content-type: text/plain; charset=UTF-8
        Hard Coded mail
      
        --boundaryboundary--
      
    • And, please encode the above request body to the websafe-base64 data.

    When these points are reflected in your script, it becomes as follows.

    Modified script:

    var user_id = "me";
    var text = "Hard Coded mail";
    
    var message = 'MIME-Version: 1.0\r\n' +
      'Content-type: multipart/alternative; boundary=boundaryboundary\r\n\r\n' +
      '--boundaryboundary\r\n' +
      'Content-type: text/plain; charset=UTF-8\r\n' +
      text + "\r\n\r\n" +
      '--boundaryboundary--';
    var blob = new Blob([message], {type:"text/plain"});
    var f = new FileReader();
    f.readAsDataURL(blob);
    f.onload = d => {
      var base64 = d.target.result.replace(/_/g, '/').replace(/-/g, '+').split(",")[1];
      axios.post(`https://gmail.googleapis.com/gmail/v1/users/${user_id}/drafts`,
        JSON.stringify({"message": {"raw": base64}}),
        {headers: {Authorization: `Bearer ${access_token}`, "Content-Type": "application/json"}}
      ).then((res) => {
        console.log(res.data)
      }).catch((error) => {
        console.error(error.response.data.error)
      })
    }
    

    Result:

    When this script is run, the following result is shown in the log.

    {
       "id":"###",
       "message":{
          "id":"###",
          "threadId":"###",
          "labelIds":[
             "DRAFT"
          ]
       }
    }
    

    Reference:

    Added:

    As another method, how about the following modified script? In this modification, blob is not used. And the content type of message/rfc822 is used. And in this case, the endpoint is changed to https://gmail.googleapis.com/upload/gmail/v1/users/${user_id}/drafts. Please be careful about this.

    Modified script:

    var user_id = "me";
    var text = "Hard Coded mail";
    
    var message = 'MIME-Version: 1.0\r\n' +
      'Content-type: multipart/alternative; boundary=boundaryboundary\r\n\r\n' +
      '--boundaryboundary\r\n' +
      'Content-type: text/plain; charset=UTF-8\r\n' +
      text + "\r\n\r\n" +
      '--boundaryboundary--';
    axios.post(`https://gmail.googleapis.com/upload/gmail/v1/users/${user_id}/drafts`,
      message,
      {headers: {Authorization: `Bearer ${access_token}`, "Content-Type": "message/rfc822"}}
    ).then((res) => {
      console.log(JSON.stringify(res.data))
    }).catch((error) => {
      console.error(error.response.data.error)
    })