1

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--';
HCKRMVT
  • 355
  • 3
  • 10

1 Answers1

2

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)
})
Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • Thnks for the detailed and step by step answer. Though I'm still going through an issue with the ```new Blob(...)``` that is ```not a constructor (TypeError)```. I've tried adding the following require as suggested in a few SO an GH threads, but nothing successful. ```const Blob = require('buffer')``` – HCKRMVT Mar 02 '22 at 10:06
  • 1
    @HCKRMVT Thank you for replying. I apologize for the inconvenience. In that case, I added one more modified script. Could you please confirm it? In that case, the endpoint is changed and the blob is not used. – Tanaike Mar 02 '22 at 12:05
  • Thank you in the first place, no worries ! And indeed it works, the log I get is the same as you show in the **Result** and after checking, a new mail appears in my draft inbox. – HCKRMVT Mar 03 '22 at 10:34
  • 1
    @HCKRMVT Thank you for replying. I'm glad your issue was resolved. Thank you, too. – Tanaike Mar 03 '22 at 12:27
  • do the new fields in the message I've added to 'EDIT(1)' message seem correct to you ? I've followed [this](https://stackoverflow.com/a/52310846/10648704) thread to bring these modifications, but ultimately they don't show up in my gmail draft. – HCKRMVT Mar 03 '22 at 14:24
  • @HCKRMVT I have to apologize for my poor English skill. From your reply, if my proposed script was not useful, I have to apologize. – Tanaike Mar 04 '22 at 00:18