{
  "openapi": "3.1.0",
  "info": {
    "title": "Canvas API",
    "version": "1.0.0",
    "summary": "A free online whiteboard (a free Miro alternative) for people and their AI assistants.",
    "description": "Start a whiteboard from a flowchart, a mind map, columns of sticky notes or a template, laid out automatically, and get back its edit, view and admin links. Read a board back as shapes, arrows and text. With the admin key, rename a board, turn its links off, make a new edit link or delete it. No account and no API key. Errors are JSON with an error sentence that says what to change. Full guide: https://canvas-free.openapps.xyz/llms.txt",
    "contact": {
      "name": "OpenApps",
      "url": "https://free-apps.openapps.xyz/"
    }
  },
  "servers": [
    {
      "url": "https://functions.bounded.sh/apps/788ba0fa01083b300ad50913/api"
    }
  ],
  "externalDocs": {
    "url": "https://canvas-free.openapps.xyz/llms.txt",
    "description": "llms.txt guide"
  },
  "paths": {
    "/": {
      "get": {
        "operationId": "listRoutes",
        "summary": "List the routes and the board templates",
        "responses": {
          "200": {
            "description": "Routes and templates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "about": {
                      "type": "string"
                    },
                    "docs": {
                      "type": "string",
                      "format": "uri"
                    },
                    "openapi": {
                      "type": "string",
                      "format": "uri"
                    },
                    "routes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "templates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "summary": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "name": "Canvas by OpenApps",
                  "templates": [
                    {
                      "id": "retro",
                      "name": "Retro",
                      "summary": "What went well, what could be better, and action items."
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/boards": {
      "post": {
        "operationId": "createBoard",
        "summary": "Start a board from a diagram, sticky notes or a template",
        "description": "Send one of nodes (with edges), columns or template; send {} for an empty board. The board is laid out and drawn the first time someone opens one of its links. Give the person editUrl; keep adminUrl for the owner.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewBoard"
              },
              "examples": {
                "flowchart": {
                  "summary": "A flowchart",
                  "value": {
                    "title": "Checkout flow",
                    "nodes": [
                      {
                        "id": "cart",
                        "label": "Cart"
                      },
                      {
                        "id": "pay",
                        "label": "Paid?",
                        "shape": "diamond",
                        "color": "yellow"
                      },
                      {
                        "id": "done",
                        "label": "Order placed",
                        "shape": "ellipse",
                        "color": "green"
                      }
                    ],
                    "edges": [
                      {
                        "from": "cart",
                        "to": "pay"
                      },
                      {
                        "from": "pay",
                        "to": "done",
                        "label": "Yes"
                      }
                    ]
                  }
                },
                "mindmap": {
                  "summary": "A mind map",
                  "value": {
                    "title": "Launch plan",
                    "direction": "right",
                    "nodes": [
                      {
                        "id": "c",
                        "label": "Launch",
                        "shape": "ellipse",
                        "color": "yellow"
                      },
                      {
                        "id": "m",
                        "label": "Marketing",
                        "color": "blue"
                      },
                      {
                        "id": "p",
                        "label": "Product",
                        "color": "green"
                      }
                    ],
                    "edges": [
                      {
                        "from": "c",
                        "to": "m"
                      },
                      {
                        "from": "c",
                        "to": "p"
                      }
                    ]
                  }
                },
                "notes": {
                  "summary": "Sticky notes in columns",
                  "value": {
                    "title": "Sprint 12 retro",
                    "columns": [
                      {
                        "name": "Went well",
                        "color": "green",
                        "notes": [
                          "Shipped search"
                        ]
                      },
                      {
                        "name": "To improve",
                        "color": "red",
                        "notes": [
                          "Flaky tests"
                        ]
                      }
                    ]
                  }
                },
                "template": {
                  "summary": "A template",
                  "value": {
                    "template": "retro"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new board and its links",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedBoard"
                },
                "example": {
                  "id": "Hs7kQEznzuQqAmQ3AkTp",
                  "title": "Checkout flow",
                  "viewUrl": "https://canvas-free.openapps.xyz/b/Hs7kQEznzuQqAmQ3AkTp",
                  "editUrl": "https://canvas-free.openapps.xyz/b/Hs7kQEznzuQqAmQ3AkTp#edit=AvPT9yPZ7gydBfHfiemamSwo",
                  "adminUrl": "https://canvas-free.openapps.xyz/b/Hs7kQEznzuQqAmQ3AkTp/admin#k=9P3wq7bKdNzT5mLr2xVfYs8c",
                  "editKey": "AvPT9yPZ7gydBfHfiemamSwo",
                  "adminKey": "9P3wq7bKdNzT5mLr2xVfYs8c",
                  "startsWith": "a diagram of 3 shapes and 2 arrows, laid out top to bottom",
                  "share": "Send editUrl to people who should draw on the board and viewUrl to people who should only look. Keep adminUrl private: it renames the board, turns its links off and deletes it. The board is drawn the first time someone opens editUrl."
                }
              }
            }
          },
          "400": {
            "description": "Something to fix, in one sentence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "edges[0] must connect two node ids from nodes (got \"a\" to \"zzz\")."
                }
              }
            }
          }
        }
      }
    },
    "/boards/{boardId}": {
      "get": {
        "operationId": "readBoard",
        "summary": "Read what is on a board",
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "description": "The part of a board link after /b/.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{16,32}$"
            }
          },
          {
            "name": "x-edit-key",
            "in": "header",
            "required": false,
            "description": "The edit key (the part of the edit link after #edit=). Needed only when the view link is off.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-admin-key",
            "in": "header",
            "required": false,
            "description": "The admin key. Also accepted for reading.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The board and its contents",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BoardRead"
                },
                "example": {
                  "board": {
                    "id": "Hs7kQEznzuQqAmQ3AkTp",
                    "title": "Checkout flow",
                    "editLink": "on",
                    "viewLink": "on",
                    "viewUrl": "https://canvas-free.openapps.xyz/b/Hs7kQEznzuQqAmQ3AkTp"
                  },
                  "contents": {
                    "frames": [],
                    "shapes": [
                      {
                        "id": "s-n0",
                        "shape": "rectangle",
                        "text": "Cart",
                        "color": "white",
                        "x": 40,
                        "y": 0,
                        "width": 150,
                        "height": 70
                      },
                      {
                        "id": "s-n1",
                        "shape": "diamond",
                        "text": "Paid?",
                        "color": "yellow",
                        "x": 20,
                        "y": 150,
                        "width": 240,
                        "height": 112
                      }
                    ],
                    "arrows": [
                      {
                        "id": "s-a0",
                        "from": "Cart",
                        "to": "Paid?"
                      }
                    ],
                    "texts": [],
                    "drawings": 0,
                    "images": 0,
                    "elements": 5
                  }
                }
              }
            }
          },
          "403": {
            "description": "Something to fix, in one sentence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "This board's view link is off. Send its admin key in x-admin-key, or its edit key in x-edit-key while the edit link is on."
                }
              }
            }
          },
          "404": {
            "description": "Something to fix, in one sentence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "No board with that id. The id is the part of the link after /b/."
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "changeBoard",
        "summary": "Rename a board, turn its links on or off, or make a new edit link",
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "description": "The part of a board link after /b/.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{16,32}$"
            }
          },
          {
            "name": "x-admin-key",
            "in": "header",
            "required": true,
            "description": "The admin key: the part of the admin link after #k=.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BoardChange"
              },
              "example": {
                "title": "Checkout v2",
                "viewLink": false,
                "newEditLink": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The board with its current links",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "board": {
                      "$ref": "#/components/schemas/BoardInfo"
                    }
                  }
                },
                "example": {
                  "board": {
                    "id": "Hs7kQEznzuQqAmQ3AkTp",
                    "title": "Checkout v2",
                    "editLink": "on",
                    "viewLink": "off",
                    "viewUrl": "https://canvas-free.openapps.xyz/b/Hs7kQEznzuQqAmQ3AkTp",
                    "editUrl": "https://canvas-free.openapps.xyz/b/Hs7kQEznzuQqAmQ3AkTp#edit=AvPT9yPZ7gydBfHfiemamSwo",
                    "adminUrl": "https://canvas-free.openapps.xyz/b/Hs7kQEznzuQqAmQ3AkTp/admin#k=9P3wq7bKdNzT5mLr2xVfYs8c"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Something to fix, in one sentence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Send title, editLink, viewLink or newEditLink."
                }
              }
            }
          },
          "401": {
            "description": "Something to fix, in one sentence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Changing a board needs its admin key in the x-admin-key header."
                }
              }
            }
          },
          "403": {
            "description": "Something to fix, in one sentence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "That admin key does not match this board. It is the part of the admin link after #k=."
                }
              }
            }
          },
          "404": {
            "description": "Something to fix, in one sentence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "No board with that id. The id is the part of the link after /b/."
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteBoard",
        "summary": "Delete a board and everything on it",
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "description": "The part of a board link after /b/.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{16,32}$"
            }
          },
          {
            "name": "x-admin-key",
            "in": "header",
            "required": true,
            "description": "The admin key: the part of the admin link after #k=.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "deleted": true,
                  "id": "Hs7kQEznzuQqAmQ3AkTp",
                  "note": "The board and everything on it are gone. Its links no longer open anything."
                }
              }
            }
          },
          "401": {
            "description": "Something to fix, in one sentence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Deleting a board needs its admin key in the x-admin-key header."
                }
              }
            }
          },
          "403": {
            "description": "Something to fix, in one sentence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "That admin key does not match this board. It is the part of the admin link after #k=."
                }
              }
            }
          },
          "404": {
            "description": "Something to fix, in one sentence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "No board with that id. The id is the part of the link after /b/."
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "A sentence that says what to change."
          }
        }
      },
      "Node": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 80
          },
          "label": {
            "type": "string",
            "maxLength": 200,
            "description": "Defaults to the id."
          },
          "shape": {
            "type": "string",
            "enum": [
              "rectangle",
              "ellipse",
              "diamond"
            ],
            "default": "rectangle"
          },
          "color": {
            "type": "string",
            "enum": [
              "yellow",
              "green",
              "blue",
              "red",
              "violet",
              "orange",
              "gray",
              "white"
            ],
            "default": "white"
          }
        }
      },
      "Edge": {
        "type": "object",
        "required": [
          "from",
          "to"
        ],
        "properties": {
          "from": {
            "type": "string",
            "description": "A node id."
          },
          "to": {
            "type": "string",
            "description": "A node id."
          },
          "label": {
            "type": "string",
            "maxLength": 200
          }
        }
      },
      "Column": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "color": {
            "type": "string",
            "enum": [
              "yellow",
              "green",
              "blue",
              "red",
              "violet",
              "orange",
              "gray",
              "white"
            ],
            "default": "yellow"
          },
          "notes": {
            "type": "array",
            "maxItems": 60,
            "items": {
              "type": "string",
              "maxLength": 300
            },
            "description": "An empty string is an empty note."
          }
        }
      },
      "NewBoard": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 120,
            "description": "Defaults to the template's name, or Untitled board."
          },
          "heading": {
            "type": "string",
            "maxLength": 200,
            "description": "Big text above a diagram or columns."
          },
          "nodes": {
            "type": "array",
            "maxItems": 150,
            "items": {
              "$ref": "#/components/schemas/Node"
            }
          },
          "edges": {
            "type": "array",
            "maxItems": 300,
            "items": {
              "$ref": "#/components/schemas/Edge"
            }
          },
          "direction": {
            "type": "string",
            "enum": [
              "down",
              "right"
            ],
            "default": "down",
            "description": "Use right for mind maps."
          },
          "columns": {
            "type": "array",
            "maxItems": 12,
            "items": {
              "$ref": "#/components/schemas/Column"
            }
          },
          "template": {
            "type": "string",
            "enum": [
              "brainstorm",
              "retro",
              "user-flow",
              "weekly-plan"
            ]
          }
        }
      },
      "CreatedBoard": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "viewUrl": {
            "type": "string",
            "format": "uri"
          },
          "editUrl": {
            "type": "string",
            "format": "uri"
          },
          "adminUrl": {
            "type": "string",
            "format": "uri"
          },
          "editKey": {
            "type": "string"
          },
          "adminKey": {
            "type": "string"
          },
          "startsWith": {
            "type": "string"
          },
          "share": {
            "type": "string"
          }
        }
      },
      "BoardInfo": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "editLink": {
            "type": "string",
            "enum": [
              "on",
              "off"
            ]
          },
          "viewLink": {
            "type": "string",
            "enum": [
              "on",
              "off"
            ]
          },
          "viewUrl": {
            "type": "string",
            "format": "uri"
          },
          "editUrl": {
            "type": "string",
            "format": "uri",
            "description": "Only when your key allows it."
          },
          "adminUrl": {
            "type": "string",
            "format": "uri",
            "description": "Only with the admin key."
          }
        }
      },
      "BoardRead": {
        "type": "object",
        "properties": {
          "board": {
            "$ref": "#/components/schemas/BoardInfo"
          },
          "contents": {
            "type": [
              "object",
              "null"
            ],
            "description": "Null until the board is first opened.",
            "properties": {
              "frames": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "items": {
                      "type": "integer"
                    }
                  }
                }
              },
              "shapes": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "shape": {
                      "type": "string"
                    },
                    "text": {
                      "type": "string"
                    },
                    "color": {
                      "type": "string"
                    },
                    "frame": {
                      "type": "string"
                    },
                    "x": {
                      "type": "number"
                    },
                    "y": {
                      "type": "number"
                    },
                    "width": {
                      "type": "number"
                    },
                    "height": {
                      "type": "number"
                    }
                  }
                }
              },
              "arrows": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "text": {
                      "type": "string"
                    }
                  }
                }
              },
              "texts": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "text": {
                      "type": "string"
                    },
                    "x": {
                      "type": "number"
                    },
                    "y": {
                      "type": "number"
                    }
                  }
                }
              },
              "drawings": {
                "type": "integer"
              },
              "images": {
                "type": "integer"
              },
              "elements": {
                "type": "integer"
              }
            }
          },
          "startsWith": {
            "type": "string"
          },
          "note": {
            "type": "string",
            "description": "Says why contents is null, or that contents covers only the first 5000 elements of a very large board."
          }
        }
      },
      "BoardChange": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 120
          },
          "editLink": {
            "type": "boolean"
          },
          "viewLink": {
            "type": "boolean"
          },
          "newEditLink": {
            "type": "boolean",
            "const": true
          }
        }
      }
    }
  }
}
