Google Calendar API v3 always returns BadRequest when creating events

Tags: ,

I created a shared calendar and want to add events to the calendar.

I created a project and set up a Service Account

Then I shared the calendar to the Service Account as owner.

Then I noticed

Service Account must manually add shared calendar

as described here and

So I wrote a code:

  fun addCalendarToServiceAccount() {

    val calList1: CalendarList = calendar.calendarList().list().execute()"calList1 = {}", calList1)

    val inserted = calendar.calendarList().insert(CalendarListEntry().setId(calendarId)).execute()"inserted = {}", inserted)

    val calList2: CalendarList = calendar.calendarList().list().execute()"calList2 = {}", calList2)

It works perfectly. When first called, I can see calList1 is empty, and calList2 contains something.

Then I manually insert one event to the calendar (with google calendar WEB UI), I want to check if I can retrieve the event:

  fun listEvents() {
    val events: Events ="events = {}", events)
    events.items.forEachIndexed { index, e ->"Event [index = {}] , event = {}", index, e)

It also works.


         "summary":"xxx  test1",

Then I want to programmatically insert something, like the API example shows:

  fun testInsertEvent() {
    val now =
    val zoneId = ZoneId.of("Asia/Taipei")
    val fromDate = Date.from(now.atZone(zoneId).toInstant())
    val endDate = Date.from(now.plusMinutes(60).atZone(zoneId).toInstant())

    val event = Event()
      .setSummary("Google I/O 2015")
      .setLocation("800 Howard St., San Francisco, CA 94103")
      .setDescription("A chance to hear more about Google's developer products.")
      .setStart(EventDateTime().setDate(DateTime(fromDate, TimeZone.getTimeZone(zoneId))))
      .setEnd(EventDateTime().setDate(DateTime(endDate, TimeZone.getTimeZone(zoneId))))"before insert event : {}", event)

    val eventResult: Event =, event).execute()"eventResult = {}", eventResult)

I can see the client truly POST to google’e endpoint:

Logs caught by IDEA

The body is:

   "description":"A chance to hear more about Google's developer products.",
   "location":"800 Howard St., San Francisco, CA 94103",
   "summary":"Google I/O 2015"

But google just replied 400 BadRequest, without any further description:

2020-08-18 10:32:15.974 [main] INFO  c.g.a.c.h.HttpResponse - -------------- RESPONSE --------------
HTTP/1.1 400 Bad Request
Transfer-Encoding: chunked
Alt-Svc: h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
Server: ESF
X-Content-Type-Options: nosniff
Pragma: no-cache
Date: Tue, 18 Aug 2020 02:32:15 GMT
X-Frame-Options: SAMEORIGIN
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Encoding: gzip
Vary: Referer
Vary: X-Origin
Vary: Origin
Expires: Mon, 01 Jan 1990 00:00:00 GMT
X-XSS-Protection: 0
Content-Type: application/json; charset=UTF-8

2020-08-18 10:32:15.980 [main] INFO  c.g.a.c.u.LoggingByteArrayOutputStream - Total: 171 bytes
2020-08-18 10:32:15.980 [main] INFO  c.g.a.c.u.LoggingByteArrayOutputStream - {
 "error": {
  "errors": [
    "domain": "global",
    "reason": "badRequest",
    "message": "Bad Request"
  "code": 400,
  "message": "Bad Request"

I am using the same calendar instance, can successfully addCalendarToServiceAccount() (as owner) and listEvents(). But what goes wrong when inserting an event? Did I miss anything?

Other fields are initialized as follows:

  private lateinit var calendarId: String

  private lateinit var apiKey : String

  private val httpTransport: HttpTransport by lazy {

  private val jacksonFactory: JsonFactory by lazy {

  private val saCredentials: GoogleCredentials by lazy {
    javaClass.getResourceAsStream("/chancer-d1de03c4c25a.json").use { iStream ->
    }.apply {

  private val requestInitializer: HttpRequestInitializer by lazy {

  private val calendar: Calendar by lazy {
    Calendar.Builder(httpTransport, jacksonFactory, requestInitializer)






You need to use start.dateTime and end.dateTime rather than and


As per the documentation: The date, in the format “yyyy-mm-dd”, if this is an all-day event.

end.dateTime: The time, as a combined date-time value (formatted according to RFC3339). A time zone offset is required unless a time zone is explicitly specified in timeZone. The date, in the format “yyyy-mm-dd”, if this is an all-day event.

start.dateTime: The time, as a combined date-time value (formatted according to RFC3339). A time zone offset is required unless a time zone is explicitly specified in timeZone.

Therefore, you need to change your date & time setting method from:

EventDateTime().setDate(DateTime(fromDate, TimeZone.getTimeZone(zoneId))))


EventDateTime().setDateTime(DateTime(fromDate, TimeZone.getTimeZone(zoneId))))

Which will change the request body to:

  "description": "A chance to hear more about Google's developer products.",
  "end": {
      "dateTime": "2020-08-18T11:32:00.000+08:00" // modified
  "location": "800 Howard St., San Francisco, CA 94103",
  "start": {
    "dateTime": "2020-08-18T10:32:00.000+08:00" // modified
  "summary": "Google I/O 2015"

You can see the documentation for this method here.

I hope this is helpful to you!


Source: stackoverflow