{
  "openapi": "3.0.3",
  "info": {
    "title": "Nimbly resource REST API",
    "version": "1",
    "description": "The existing /api/v1 resource interface. Resource response keys and record fields depend on the application. Authentication and role permissions apply to every protected operation. This description covers the generic resource, token and file-upload interfaces; project-specific endpoints are defined by their applications."
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/api/v1/{resource}": {
      "get": {
        "operationId": "listResource",
        "summary": "List authorized resource records",
        "parameters": [
          {
            "name": "resource",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Resource name; fields and permissions are application-defined."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Maximum records returned."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Starting record offset."
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Example: date|asc or title|text|asc."
          },
          {
            "name": "filter",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Example: published:1."
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Search title or all fields when no title field exists."
          }
        ],
        "responses": {
          "200": {
            "description": "Operation succeeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied; needs lists the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not implemented",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "409": {
            "description": "Record already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "422": {
            "description": "Field validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "500": {
            "description": "Operation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not modified according to conditional request headers."
          }
        }
      },
      "post": {
        "operationId": "createRecord",
        "summary": "Create a resource record",
        "parameters": [
          {
            "name": "resource",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Resource name; fields and permissions are application-defined."
          }
        ],
        "responses": {
          "201": {
            "description": "Operation succeeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied; needs lists the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not implemented",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "409": {
            "description": "Record already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "422": {
            "description": "Field validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "500": {
            "description": "Operation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateRecords",
        "summary": "Update multiple records with an object keyed by UUID",
        "parameters": [
          {
            "name": "resource",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Resource name; fields and permissions are application-defined."
          }
        ],
        "responses": {
          "200": {
            "description": "Operation succeeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied; needs lists the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not implemented",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "409": {
            "description": "Record already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "422": {
            "description": "Field validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "500": {
            "description": "Operation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteResourceRecords",
        "summary": "Delete all records; requires distinct api_delete_resource_{resource} permission",
        "parameters": [
          {
            "name": "resource",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Resource name; fields and permissions are application-defined."
          }
        ],
        "responses": {
          "200": {
            "description": "Operation succeeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied; needs lists the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not implemented",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "409": {
            "description": "Record already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "422": {
            "description": "Field validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "500": {
            "description": "Operation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/{resource}/{uuid}": {
      "get": {
        "operationId": "readRecord",
        "summary": "Read one authorized resource record",
        "parameters": [
          {
            "name": "resource",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Resource name; fields and permissions are application-defined."
          },
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Record identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "Operation succeeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied; needs lists the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not implemented",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "409": {
            "description": "Record already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "422": {
            "description": "Field validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "500": {
            "description": "Operation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not modified according to conditional request headers."
          }
        }
      },
      "post": {
        "operationId": "createRecordWithId",
        "summary": "Create a record using an explicit identifier",
        "parameters": [
          {
            "name": "resource",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Resource name; fields and permissions are application-defined."
          },
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Record identifier."
          }
        ],
        "responses": {
          "201": {
            "description": "Operation succeeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied; needs lists the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not implemented",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "409": {
            "description": "Record already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "422": {
            "description": "Field validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "500": {
            "description": "Operation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateRecord",
        "summary": "Partially update a record; supplied fields change",
        "parameters": [
          {
            "name": "resource",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Resource name; fields and permissions are application-defined."
          },
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Record identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "Operation succeeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied; needs lists the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not implemented",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "409": {
            "description": "Record already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "422": {
            "description": "Field validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "500": {
            "description": "Operation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteRecord",
        "summary": "Delete one authorized resource record",
        "parameters": [
          {
            "name": "resource",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Resource name; fields and permissions are application-defined."
          },
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Record identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "Operation succeeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied; needs lists the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not implemented",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "409": {
            "description": "Record already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "422": {
            "description": "Field validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "500": {
            "description": "Operation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "404": {
            "description": "Record not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/token": {
      "post": {
        "operationId": "issueToken",
        "summary": "Authenticate a user with API access enabled; token lifetime is 600 seconds",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string",
                    "format": "password"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bearer token and creation/expiry timestamps",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Unknown account or API access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "refreshToken",
        "summary": "Refresh an unexpired bearer token",
        "responses": {
          "200": {
            "description": "Refreshed token and timestamps",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            }
          },
          "401": {
            "description": "Expired or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/.files": {
      "get": {
        "operationId": "listFiles",
        "summary": "List authorized file metadata",
        "responses": {
          "200": {
            "description": "files contains metadata keyed by UUID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "uploadFile",
        "summary": "Upload a file; identical content reuses its MD5 identifier",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "files contains the uploaded file metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "400": {
            "description": "No file supplied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "403": {
            "description": "Access denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "413": {
            "description": "Upload exceeds configured maximum",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          },
          "500": {
            "description": "Storage failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "ResourceResponse": {
        "type": "object",
        "required": [
          "code",
          "success",
          "status"
        ],
        "properties": {
          "code": {
            "type": "integer"
          },
          "success": {
            "type": "boolean"
          },
          "status": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "message": {
            "type": "string"
          },
          "needs": {
            "type": "string"
          },
          "memory_usage": {
            "type": "string"
          },
          "execution_time": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "TokenResponse": {
        "type": "object",
        "required": [
          "token",
          "token_created",
          "token_expires",
          "code",
          "success",
          "status"
        ],
        "properties": {
          "code": {
            "type": "integer"
          },
          "success": {
            "type": "boolean"
          },
          "status": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "message": {
            "type": "string"
          },
          "needs": {
            "type": "string"
          },
          "memory_usage": {
            "type": "string"
          },
          "execution_time": {
            "type": "string"
          },
          "token": {
            "type": "string"
          },
          "token_created": {
            "type": "integer"
          },
          "token_expires": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      }
    }
  }
}
