{
  "openapi": "3.1.0",
  "info": {
    "title": "Build API",
    "version": "0.0.0",
    "description": "Pre-release. Breaking changes are expected while the API is at v0; do not treat it as stable."
  },
  "servers": [
    {
      "url": "https://build.vu.city",
      "description": "Build API"
    }
  ],
  "components": {
    "securitySchemes": {
      "oauth2ClientCredentials": {
        "type": "oauth2",
        "description": "Machine-to-machine only. There is no user-facing authorization step.",
        "flows": {
          "clientCredentials": {
            "tokenUrl": "https://build.vu.city/oauth/token",
            "scopes": {
              "volume:read": "List and read VU.CITY Drive volumes",
              "sync:create": "Create a data source sync for a file"
            }
          }
        }
      }
    },
    "schemas": {
      "VolumeList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Volume"
            }
          }
        },
        "required": [
          "data"
        ],
        "additionalProperties": false
      },
      "Volume": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "vol_01hq3n8k4e2r7yfp5m6z9x0abc"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "My Organisation Data"
            ]
          }
        },
        "required": [
          "id",
          "name"
        ],
        "additionalProperties": false
      },
      "Unauthorized": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "examples": [
              "Unauthorized"
            ]
          }
        },
        "required": [
          "message"
        ]
      },
      "Forbidden": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "examples": [
              "insufficient_scope"
            ]
          },
          "error_description": {
            "type": "string",
            "examples": [
              "This endpoint requires all of: scope:one scope:two scope:three."
            ]
          }
        },
        "required": [
          "error",
          "error_description"
        ]
      },
      "InternalError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "examples": [
              "internal_error"
            ]
          },
          "error_description": {
            "type": "string",
            "examples": [
              "The server produced a response that does not match its published schema."
            ]
          }
        },
        "required": [
          "error",
          "error_description"
        ]
      },
      "FileSyncUploadUrl": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "upload_url": {
                "type": "string",
                "format": "uri",
                "examples": [
                  "https://uploads.vu.city/files/file_01hq3n8k4e2r7yfp5m6z9x0abc/..."
                ],
                "description": "A presigned upload URL. PUT the raw file bytes to this URL as the request body, in a single request - do not use multipart upload. No Authorization header or other credentials are needed; the signature is already embedded in the URL. Valid for one hour from issue and accepts files up to 5 GB."
              }
            },
            "required": [
              "upload_url"
            ]
          }
        },
        "required": [
          "data"
        ],
        "additionalProperties": false
      },
      "FileNotFound": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "examples": [
              "not_found"
            ]
          },
          "error_description": {
            "type": "string",
            "examples": [
              "No file exists with this id."
            ]
          }
        },
        "required": [
          "error",
          "error_description"
        ],
        "additionalProperties": false
      },
      "SyncNotEnabled": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "examples": [
              "sync_not_enabled"
            ]
          },
          "error_description": {
            "type": "string",
            "examples": [
              "This file does not have syncing enabled in the Hub."
            ]
          }
        },
        "required": [
          "error",
          "error_description"
        ],
        "additionalProperties": false
      }
    },
    "parameters": {}
  },
  "paths": {
    "/v0/volumes": {
      "get": {
        "tags": [
          "Volumes"
        ],
        "summary": "List volumes",
        "description": "Returns the VU.CITY Drive volumes the authenticated client can read. Currently always empty.",
        "security": [
          {
            "oauth2ClientCredentials": [
              "volume:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The volumes readable by this client.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeList"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "The token does not carry the required scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Forbidden"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalError"
                }
              }
            }
          }
        }
      }
    },
    "/v0/files/{fileId}/sync": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Start a file sync",
        "description": "Issues a one-time upload URL for syncing an external data source into a VU.CITY file. The file must have syncing enabled in the Hub. Accepts files up to 5 GB; the returned URL expires one hour after issue.",
        "security": [
          {
            "oauth2ClientCredentials": [
              "sync:create"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "example": "file_01hq3n8k4e2r7yfp5m6z9x0abc",
            "name": "fileId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Upload URL issued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileSyncUploadUrl"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "The token does not carry the required scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Forbidden"
                }
              }
            }
          },
          "404": {
            "description": "No file exists with this id, or it belongs to a different organisation than the authenticated client.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileNotFound"
                }
              }
            }
          },
          "409": {
            "description": "The file does not have syncing enabled in the Hub.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SyncNotEnabled"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalError"
                }
              }
            }
          }
        }
      }
    }
  }
}
