when-free.openapps.xyz

Source

This app’s code and revision history are public. Download a copy or explore the files below.

Public
The app’s code and revision history are public.
Owned app · rev 2315d847681e
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 3e1cb86700a5e96238d7a2f1
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
← All files
public/openapi.json
{
  "openapi": "3.1.0",
  "info": {
    "title": "When by OpenApps",
    "version": "1.1.0",
    "summary": "Free group scheduling polls: suggest times, collect yes, if need be or no from everyone, and pick the final time.",
    "description": "When is a free Doodle alternative with no account, no key and no limit on polls. A poll's id is its secret, exactly like its link: anyone with it can read the poll, answer it and comment. The ownerKey returned when a poll is made lets the caller edit it, close it and pick the final time; send it in the x-owner-key header and keep it private. Times without an offset are read in the poll's time zone. Errors are JSON with an `error` sentence that says what to change. CORS is open.",
    "contact": {
      "name": "OpenApps",
      "url": "https://free-apps.openapps.xyz/"
    },
    "license": {
      "name": "Free to use"
    }
  },
  "externalDocs": {
    "description": "Guide for assistants",
    "url": "https://when-free.openapps.xyz/llms.txt"
  },
  "servers": [
    {
      "url": "https://functions.bounded.sh/apps/3e1cb86700a5e96238d7a2f1/api"
    }
  ],
  "components": {
    "securitySchemes": {
      "ownerKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-owner-key",
        "description": "The ownerKey from POST /polls (also inside the adminUrl after #k=)."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "A sentence that says what went wrong and what to change."
          }
        },
        "required": [
          "error"
        ]
      },
      "Answer": {
        "type": "string",
        "enum": [
          "yes",
          "maybe",
          "no"
        ]
      },
      "Option": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "start": {
            "type": "string",
            "description": "Start in the poll's time zone, for timed options."
          },
          "end": {
            "type": "string",
            "description": "End in the poll's time zone, when the option has one."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "The date, for all-day options."
          },
          "allDay": {
            "type": "boolean"
          },
          "label": {
            "type": "string",
            "description": "Readable, with the time zone named."
          },
          "yes": {
            "type": "integer"
          },
          "maybe": {
            "type": "integer"
          },
          "no": {
            "type": "integer"
          },
          "best": {
            "type": "boolean",
            "description": "True for the option(s) with the most yes, then the most if need be."
          }
        },
        "required": [
          "id",
          "label",
          "yes",
          "maybe",
          "no",
          "best"
        ]
      },
      "PollSummary": {
        "type": "object",
        "properties": {
          "poll": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "location": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "timezone": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "The link to send to participants."
              },
              "status": {
                "type": "string",
                "enum": [
                  "open",
                  "closed",
                  "decided"
                ]
              }
            }
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Option"
            }
          },
          "participants": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "answers": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/Answer"
                  },
                  "description": "Option id to answer. A time added after someone answered has no answer from them."
                }
              }
            }
          },
          "best": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Option ids of the best time(s); empty until someone can make a time."
          },
          "chosen": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Option"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "ics": {
                        "type": "string",
                        "format": "uri"
                      },
                      "googleCalendar": {
                        "type": "string",
                        "format": "uri"
                      },
                      "outlookCalendar": {
                        "type": "string",
                        "format": "uri"
                      }
                    }
                  }
                ]
              }
            ],
            "description": "The final time once the organizer picks it, with calendar links."
          },
          "comments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "text": {
                  "type": "string"
                },
                "at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "description": "Oldest first."
          }
        },
        "required": [
          "poll",
          "options",
          "participants",
          "best",
          "chosen",
          "comments"
        ]
      }
    }
  },
  "paths": {
    "/": {
      "get": {
        "operationId": "listRoutes",
        "summary": "List the routes and where the docs are",
        "responses": {
          "200": {
            "description": "The routes.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "about": {
                      "type": "string"
                    },
                    "docs": {
                      "type": "string"
                    },
                    "openapi": {
                      "type": "string"
                    },
                    "routes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/polls": {
      "post": {
        "operationId": "createPoll",
        "summary": "Make a poll and get its link, admin link and owner key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "location": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "timezone": {
                    "type": "string",
                    "description": "The organizer's IANA time zone. Defaults to UTC.",
                    "example": "America/New_York"
                  },
                  "options": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 500,
                    "items": {
                      "oneOf": [
                        {
                          "type": "string",
                          "description": "An ISO time (\"2026-10-21T15:00:00-04:00\", or \"2026-10-21T15:00\" read in the poll's time zone), or a date (\"2026-10-23\") for all day."
                        },
                        {
                          "type": "object",
                          "properties": {
                            "start": {
                              "type": "string"
                            },
                            "end": {
                              "type": "string",
                              "description": "Optional. After start, at most a week later."
                            }
                          },
                          "required": [
                            "start"
                          ],
                          "additionalProperties": false
                        },
                        {
                          "type": "object",
                          "properties": {
                            "date": {
                              "type": "string",
                              "format": "date"
                            },
                            "allDay": {
                              "type": "boolean",
                              "const": true
                            }
                          },
                          "required": [
                            "date"
                          ],
                          "additionalProperties": false
                        }
                      ]
                    }
                  }
                },
                "required": [
                  "title",
                  "options"
                ]
              },
              "example": {
                "title": "Team lunch",
                "location": "Cafe Rio",
                "timezone": "America/New_York",
                "options": [
                  "2026-10-27T12:00",
                  {
                    "start": "2026-10-27T15:00",
                    "end": "2026-10-27T16:00"
                  },
                  {
                    "date": "2026-10-30",
                    "allDay": true
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The poll was made. Give the person url to share and adminUrl to keep; keep ownerKey to manage it.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "pollId": {
                          "type": "string"
                        },
                        "url": {
                          "type": "string"
                        },
                        "adminUrl": {
                          "type": "string"
                        },
                        "ownerKey": {
                          "type": "string"
                        },
                        "share": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "pollId",
                        "url",
                        "adminUrl",
                        "ownerKey"
                      ]
                    },
                    {
                      "$ref": "#/components/schemas/PollSummary"
                    }
                  ]
                },
                "example": {
                  "pollId": "GckysfetvdBJJor43QN4",
                  "url": "https://when-free.openapps.xyz/p/GckysfetvdBJJor43QN4",
                  "adminUrl": "https://when-free.openapps.xyz/p/GckysfetvdBJJor43QN4/admin#k=GZtDsR6zDA4L5XoJ9yCMvFbM",
                  "ownerKey": "GZtDsR6zDA4L5XoJ9yCMvFbM",
                  "share": "Send https://when-free.openapps.xyz/p/GckysfetvdBJJor43QN4 to the group. Keep the adminUrl and ownerKey private: they let you edit the poll and pick the final time.",
                  "poll": {
                    "id": "GckysfetvdBJJor43QN4",
                    "title": "Team lunch",
                    "description": null,
                    "location": "Cafe Rio",
                    "timezone": "America/New_York",
                    "url": "https://when-free.openapps.xyz/p/GckysfetvdBJJor43QN4",
                    "status": "open"
                  },
                  "options": [
                    {
                      "id": "NWpoik",
                      "start": "2026-10-27T12:00:00-04:00",
                      "label": "Tue, Oct 27, 12:00 PM EDT",
                      "yes": 0,
                      "maybe": 0,
                      "no": 0,
                      "best": false
                    },
                    {
                      "id": "B7oXVp",
                      "start": "2026-10-27T15:00:00-04:00",
                      "end": "2026-10-27T16:00:00-04:00",
                      "label": "Tue, Oct 27, 3:00 PM to 4:00 PM EDT",
                      "yes": 0,
                      "maybe": 0,
                      "no": 0,
                      "best": false
                    },
                    {
                      "id": "mXQrNm",
                      "date": "2026-10-30",
                      "allDay": true,
                      "label": "Fri, Oct 30, all day",
                      "yes": 0,
                      "maybe": 0,
                      "no": 0,
                      "best": false
                    }
                  ],
                  "participants": [],
                  "best": [],
                  "chosen": null,
                  "comments": []
                }
              }
            }
          },
          "400": {
            "description": "Bad input.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "options must be a list with at least one time."
                }
              }
            }
          }
        }
      }
    },
    "/polls/{pollId}": {
      "parameters": [
        {
          "name": "pollId",
          "in": "path",
          "required": true,
          "description": "The poll's id: the last part of its link. Anyone with it can read and answer the poll.",
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{8,32}$"
          },
          "example": "GckysfetvdBJJor43QN4"
        }
      ],
      "get": {
        "operationId": "getPoll",
        "summary": "Read a poll: options with counts, everyone's answers, the best time, the final time and comments",
        "responses": {
          "200": {
            "description": "The poll.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PollSummary"
                },
                "example": {
                  "poll": {
                    "id": "GckysfetvdBJJor43QN4",
                    "title": "Team lunch",
                    "description": null,
                    "location": "Cafe Rio",
                    "timezone": "America/New_York",
                    "url": "https://when-free.openapps.xyz/p/GckysfetvdBJJor43QN4",
                    "status": "open"
                  },
                  "options": [
                    {
                      "id": "NWpoik",
                      "start": "2026-10-27T12:00:00-04:00",
                      "label": "Tue, Oct 27, 12:00 PM EDT",
                      "yes": 2,
                      "maybe": 1,
                      "no": 0,
                      "best": true
                    },
                    {
                      "id": "B7oXVp",
                      "start": "2026-10-27T15:00:00-04:00",
                      "end": "2026-10-27T16:00:00-04:00",
                      "label": "Tue, Oct 27, 3:00 PM to 4:00 PM EDT",
                      "yes": 1,
                      "maybe": 0,
                      "no": 2,
                      "best": false
                    },
                    {
                      "id": "mXQrNm",
                      "date": "2026-10-30",
                      "allDay": true,
                      "label": "Fri, Oct 30, all day",
                      "yes": 1,
                      "maybe": 1,
                      "no": 1,
                      "best": false
                    }
                  ],
                  "participants": [
                    {
                      "name": "Sam",
                      "answers": {
                        "NWpoik": "yes",
                        "B7oXVp": "yes",
                        "mXQrNm": "no"
                      }
                    },
                    {
                      "name": "Priya",
                      "answers": {
                        "NWpoik": "yes",
                        "B7oXVp": "no",
                        "mXQrNm": "maybe"
                      }
                    },
                    {
                      "name": "Jordan",
                      "answers": {
                        "NWpoik": "maybe",
                        "B7oXVp": "no",
                        "mXQrNm": "yes"
                      }
                    }
                  ],
                  "best": [
                    "NWpoik"
                  ],
                  "chosen": null,
                  "comments": [
                    {
                      "name": "Priya",
                      "text": "Tuesday works best for me.",
                      "at": "2026-10-19T14:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such poll.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "No poll with that id."
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "editPoll",
        "summary": "Edit a poll (owner)",
        "security": [
          {
            "ownerKey": []
          }
        ],
        "description": "options replaces the list; times that stay the same keep their answers. Send null to clear description or location.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "location": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "timezone": {
                    "type": "string"
                  },
                  "options": {
                    "type": "array",
                    "items": {
                      "oneOf": [
                        {
                          "type": "string",
                          "description": "An ISO time (\"2026-10-21T15:00:00-04:00\", or \"2026-10-21T15:00\" read in the poll's time zone), or a date (\"2026-10-23\") for all day."
                        },
                        {
                          "type": "object",
                          "properties": {
                            "start": {
                              "type": "string"
                            },
                            "end": {
                              "type": "string",
                              "description": "Optional. After start, at most a week later."
                            }
                          },
                          "required": [
                            "start"
                          ],
                          "additionalProperties": false
                        },
                        {
                          "type": "object",
                          "properties": {
                            "date": {
                              "type": "string",
                              "format": "date"
                            },
                            "allDay": {
                              "type": "boolean",
                              "const": true
                            }
                          },
                          "required": [
                            "date"
                          ],
                          "additionalProperties": false
                        }
                      ]
                    }
                  }
                }
              },
              "example": {
                "location": "Room 4B",
                "options": [
                  "2026-10-27T12:00",
                  "2026-10-28T12:00"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The edited poll.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PollSummary"
                }
              }
            }
          },
          "401": {
            "description": "No key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Send the poll's owner key in the x-owner-key header."
                }
              }
            }
          },
          "403": {
            "description": "Wrong key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "That owner key does not match this poll."
                }
              }
            }
          }
        }
      }
    },
    "/polls/{pollId}/votes": {
      "parameters": [
        {
          "name": "pollId",
          "in": "path",
          "required": true,
          "description": "The poll's id: the last part of its link. Anyone with it can read and answer the poll.",
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{8,32}$"
          },
          "example": "GckysfetvdBJJor43QN4"
        }
      ],
      "post": {
        "operationId": "answerPoll",
        "summary": "Answer a poll, or change answers made through the API",
        "description": "Each call without voteId adds a new participant row and returns its voteId. Send the same voteId to change those answers. Options left out count as no. A closed poll refuses answers.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 60
                  },
                  "answers": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string",
                      "enum": [
                        "yes",
                        "maybe",
                        "if need be",
                        "no"
                      ]
                    },
                    "description": "Keys are option ids, or start times or dates that match an option."
                  },
                  "voteId": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "answers"
                ]
              },
              "example": {
                "name": "Sam",
                "answers": {
                  "NWpoik": "yes",
                  "2026-10-27T15:00": "maybe"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Answers added.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "voteId": {
                          "type": "string"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/PollSummary"
                    }
                  ]
                }
              }
            }
          },
          "200": {
            "description": "Answers changed.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "voteId": {
                          "type": "string"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/PollSummary"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad input.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "\"zzz\" is not one of this poll's options. GET /polls/GckysfetvdBJJor43QN4 lists them."
                }
              }
            }
          },
          "403": {
            "description": "The voteId belongs to a row made in the web app.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "That row was added in the web app, so only its author can change it there."
                }
              }
            }
          },
          "404": {
            "description": "No poll with that id, or no answers with that voteId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "No answers with that voteId in this poll."
                }
              }
            }
          },
          "409": {
            "description": "Closed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "This poll is closed, so answers can no longer change."
                }
              }
            }
          }
        }
      }
    },
    "/polls/{pollId}/comments": {
      "parameters": [
        {
          "name": "pollId",
          "in": "path",
          "required": true,
          "description": "The poll's id: the last part of its link. Anyone with it can read and answer the poll.",
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{8,32}$"
          },
          "example": "GckysfetvdBJJor43QN4"
        }
      ],
      "post": {
        "operationId": "commentOnPoll",
        "summary": "Add a comment everyone with the link sees",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 60
                  },
                  "text": {
                    "type": "string",
                    "maxLength": 1000
                  }
                },
                "required": [
                  "name",
                  "text"
                ]
              },
              "example": {
                "name": "Sam",
                "text": "Only after 6 for me on weekdays."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Comment added.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "commentId": {
                          "type": "string"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/PollSummary"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad input.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "text is required."
                }
              }
            }
          }
        }
      }
    },
    "/polls/{pollId}/choose": {
      "parameters": [
        {
          "name": "pollId",
          "in": "path",
          "required": true,
          "description": "The poll's id: the last part of its link. Anyone with it can read and answer the poll.",
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{8,32}$"
          },
          "example": "GckysfetvdBJJor43QN4"
        }
      ],
      "post": {
        "operationId": "pickFinalTime",
        "summary": "Pick the final time (owner); closes the poll",
        "security": [
          {
            "ownerKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "optionId": {
                    "type": "string",
                    "description": "An option id, or the option's start time or date."
                  }
                },
                "required": [
                  "optionId"
                ]
              },
              "example": {
                "optionId": "NWpoik"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The poll with chosen filled in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PollSummary"
                },
                "example": {
                  "poll": {
                    "id": "GckysfetvdBJJor43QN4",
                    "title": "Team lunch",
                    "description": null,
                    "location": "Cafe Rio",
                    "timezone": "America/New_York",
                    "url": "https://when-free.openapps.xyz/p/GckysfetvdBJJor43QN4",
                    "status": "decided"
                  },
                  "options": [
                    {
                      "id": "NWpoik",
                      "start": "2026-10-27T12:00:00-04:00",
                      "label": "Tue, Oct 27, 12:00 PM EDT",
                      "yes": 2,
                      "maybe": 1,
                      "no": 0,
                      "best": true
                    },
                    {
                      "id": "B7oXVp",
                      "start": "2026-10-27T15:00:00-04:00",
                      "end": "2026-10-27T16:00:00-04:00",
                      "label": "Tue, Oct 27, 3:00 PM to 4:00 PM EDT",
                      "yes": 1,
                      "maybe": 0,
                      "no": 2,
                      "best": false
                    },
                    {
                      "id": "mXQrNm",
                      "date": "2026-10-30",
                      "allDay": true,
                      "label": "Fri, Oct 30, all day",
                      "yes": 1,
                      "maybe": 1,
                      "no": 1,
                      "best": false
                    }
                  ],
                  "participants": [
                    {
                      "name": "Sam",
                      "answers": {
                        "NWpoik": "yes",
                        "B7oXVp": "yes",
                        "mXQrNm": "no"
                      }
                    },
                    {
                      "name": "Priya",
                      "answers": {
                        "NWpoik": "yes",
                        "B7oXVp": "no",
                        "mXQrNm": "maybe"
                      }
                    },
                    {
                      "name": "Jordan",
                      "answers": {
                        "NWpoik": "maybe",
                        "B7oXVp": "no",
                        "mXQrNm": "yes"
                      }
                    }
                  ],
                  "best": [
                    "NWpoik"
                  ],
                  "chosen": {
                    "id": "NWpoik",
                    "start": "2026-10-27T12:00:00-04:00",
                    "label": "Tue, Oct 27, 12:00 PM EDT",
                    "yes": 2,
                    "maybe": 1,
                    "no": 0,
                    "best": true,
                    "ics": "https://functions.bounded.sh/apps/3e1cb86700a5e96238d7a2f1/api/polls/GckysfetvdBJJor43QN4/ics",
                    "googleCalendar": "https://calendar.google.com/calendar/render?action=TEMPLATE&text=Team+lunch&dates=20261027T160000Z%2F20261027T170000Z",
                    "outlookCalendar": "https://outlook.live.com/calendar/0/deeplink/compose?path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&subject=Team+lunch&startdt=2026-10-27T16%3A00%3A00Z&enddt=2026-10-27T17%3A00%3A00Z"
                  },
                  "comments": [
                    {
                      "name": "Priya",
                      "text": "Tuesday works best for me.",
                      "at": "2026-10-19T14:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Unknown option.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "optionId must be one of this poll's option ids. GET /polls/GckysfetvdBJJor43QN4 lists them."
                }
              }
            }
          },
          "401": {
            "description": "No key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Send the poll's owner key in the x-owner-key header."
                }
              }
            }
          },
          "403": {
            "description": "Wrong key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "That owner key does not match this poll."
                }
              }
            }
          }
        }
      }
    },
    "/polls/{pollId}/close": {
      "parameters": [
        {
          "name": "pollId",
          "in": "path",
          "required": true,
          "description": "The poll's id: the last part of its link. Anyone with it can read and answer the poll.",
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{8,32}$"
          },
          "example": "GckysfetvdBJJor43QN4"
        }
      ],
      "post": {
        "operationId": "closePoll",
        "summary": "Stop new answers (owner)",
        "security": [
          {
            "ownerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "The closed poll.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PollSummary"
                }
              }
            }
          },
          "403": {
            "description": "Wrong key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "That owner key does not match this poll."
                }
              }
            }
          }
        }
      }
    },
    "/polls/{pollId}/reopen": {
      "parameters": [
        {
          "name": "pollId",
          "in": "path",
          "required": true,
          "description": "The poll's id: the last part of its link. Anyone with it can read and answer the poll.",
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{8,32}$"
          },
          "example": "GckysfetvdBJJor43QN4"
        }
      ],
      "post": {
        "operationId": "reopenPoll",
        "summary": "Open the poll again and clear the final time (owner)",
        "security": [
          {
            "ownerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "The open poll.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PollSummary"
                }
              }
            }
          },
          "403": {
            "description": "Wrong key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "That owner key does not match this poll."
                }
              }
            }
          }
        }
      }
    },
    "/polls/{pollId}/ics": {
      "parameters": [
        {
          "name": "pollId",
          "in": "path",
          "required": true,
          "description": "The poll's id: the last part of its link. Anyone with it can read and answer the poll.",
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{8,32}$"
          },
          "example": "GckysfetvdBJJor43QN4"
        }
      ],
      "get": {
        "operationId": "getCalendarFile",
        "summary": "A calendar file (.ics) for the final time",
        "responses": {
          "200": {
            "description": "A standard calendar file to import into any calendar app.",
            "content": {
              "text/calendar": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No final time yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "No final time has been picked for this poll yet."
                }
              }
            }
          }
        }
      }
    }
  }
}

Change history

Quality pass: comments, CSV export, share the final time, calendar menu, fixes
2315d84768 · 4 hours ago
Bounded source sync
145324066b · 1 day ago
Bounded source sync
ad9b674dac · 1 day ago
Bounded source sync
e5b69608f3 · 1 day ago
Bounded source sync
a1685914b0 · 1 day ago
Bounded source sync
7108f42d4d · 1 day ago
Bounded source sync
ffb51b672e · 1 day ago
Bounded source sync
23b5d0c53b · 1 day ago