

```json
{
  "openapi": "3.0.0",
  "info": {
    "contact": {
      "name": "Cloud Save Team",
      "url": "https://discussions.unity.com/tag/cloud-save"
    },
    "title": "Cloud Save Files Admin API",
    "version": "1.0",
    "description": "Server (admin) API surface for Unity Cloud Save Files: the endpoints that\ncarry the `environments/{environmentId}` path segment. These endpoints are\nserver-authenticated (Service Account / `unity` JWT) and authorized with\nRBAC permissions, an entitlement check, and a nested-resource check that\nthe environment belongs to the project. The player-facing API (no\nenvironment segment) lives in `files/client-v1-routes.yaml`.\n"
  },
  "servers": [
    {
      "url": "https://services.api.unity.com/cloud-save/v1/files"
    }
  ],
  "tags": [
    {
      "name": "Files",
      "description": "Associated with the Cloud Save Files service."
    }
  ],
  "paths": {
    "/projects/{projectId}/environments/{environmentId}/players/{playerId}/items": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ProjectId"
        },
        {
          "$ref": "#/components/parameters/EnvironmentId"
        },
        {
          "$ref": "#/components/parameters/PlayerId"
        }
      ],
      "get": {
        "summary": "List Player Files",
        "description": "Retrieves a list of names of all the files currently saved in storage for a player along with size (in bytes), last date modified and current WriteLock.\nOrdered alphabetically in page sizes of 20.\n",
        "operationId": "listPlayerFiles",
        "tags": [
          "Files"
        ],
        "security": [
          {
            "ServiceAccount": [
              "cloud_save.files.list"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "after",
            "description": "The key after which to retrieve the next page of files."
          },
          {
            "schema": {
              "type": "integer"
            },
            "in": "query",
            "name": "pageSize",
            "description": "The list size of the returned files, defaulted to 20."
          }
        ]
      }
    },
    "/projects/{projectId}/environments/{environmentId}/players/{playerId}/items/{key}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ProjectId"
        },
        {
          "$ref": "#/components/parameters/EnvironmentId"
        },
        {
          "$ref": "#/components/parameters/PlayerId"
        },
        {
          "$ref": "#/components/parameters/Key"
        }
      ],
      "get": {
        "summary": "Get Player File Download URL",
        "description": "Generates a signed URL that will allow the client to download the requested player file for a limited period of time. If successful, an object will be returned containing all necessary information to perform the download.",
        "operationId": "getPlayerFileDownloadUrl",
        "tags": [
          "Files"
        ],
        "security": [
          {
            "ServiceAccount": [
              "cloud_save.files.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "delete": {
        "summary": "Delete Player File",
        "description": "Deletes the specified player file. If a WriteLock is provided, the file will only be deleted if it matches the stored WriteLock.",
        "operationId": "deletePlayerFile",
        "tags": [
          "Files"
        ],
        "security": [
          {
            "ServiceAccount": [
              "cloud_save.files.delete"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "OK"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "writeLock",
            "description": "Enforces conflict checking when deleting an existing file. Omitting this field ignores write conflicts. When present, an error response will be returned if the writeLock in the request does not match the stored writeLock."
          }
        ]
      }
    },
    "/projects/{projectId}/environments/{environmentId}/players/{playerId}/items/{key}/metadata": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ProjectId"
        },
        {
          "$ref": "#/components/parameters/EnvironmentId"
        },
        {
          "$ref": "#/components/parameters/PlayerId"
        },
        {
          "$ref": "#/components/parameters/Key"
        }
      ],
      "get": {
        "summary": "Get Player File Metadata",
        "description": "Retrieves the metadata (size, date last modified and created, key, content type, and current WriteLock) of a single player file.",
        "operationId": "getPlayerFileMetadata",
        "tags": [
          "Files"
        ],
        "security": [
          {
            "ServiceAccount": [
              "cloud_save.files.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileItem"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/projects/{projectId}/environments/{environmentId}/players": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ProjectId"
        },
        {
          "$ref": "#/components/parameters/EnvironmentId"
        }
      ],
      "get": {
        "summary": "Get Players",
        "description": "Retrieves a list of all players that have files saved in storage, ordered alphabetically.",
        "operationId": "files.getPlayers",
        "tags": [
          "Files"
        ],
        "security": [
          {
            "ServiceAccount": [
              "cloud_save.files.list"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetPlayersWithFilesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "after",
            "description": "The player after which to retrieve the next page of players."
          },
          {
            "schema": {
              "type": "integer"
            },
            "in": "query",
            "name": "pageSize",
            "description": "The list size of the returned players, defaulted to 20."
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ServiceAccount": {
        "type": "http",
        "scheme": "basic",
        "description": "To get started with Authentication, please visit the [Service Account Authentication section](https://services.docs.unity.com/docs/service-account-auth).\n\n\nFor this API, you will need to add the following role(s) to your service account :\n| Role Type | Role Name | Permissions |\n| --- | --- | --- |\n| Project Level Role | Cloud Save Editor | <ul><li>cloud_save.indexes.get</li><li>cloud_save.indexes.list</li><li>cloud_save.indexes.create</li><li>cloud_save.indexes.delete</li><li>cloud_save.entities.list</li><li>cloud_save.data.read</li><li>cloud_save.data.upsert</li><li>cloud_save.data.delete</li><li>cloud_save.files.list</li><li>cloud_save.files.read</li><li>cloud_save.files.delete</li><li>cloud_save.files_env.read</li><li>cloud_save.files_env.provision</li></ul> |\n| Project Level Role | Cloud Save Viewer | <ul><li>cloud_save.indexes.get</li><li>cloud_save.indexes.list</li><li>cloud_save.entities.list</li><li>cloud_save.data.read</li><li>cloud_save.files.list</li><li>cloud_save.files.read</li><li>cloud_save.files_env.read</li></ul> |"
      }
    },
    "parameters": {
      "ProjectId": {
        "name": "projectId",
        "in": "path",
        "description": "ID of the project.",
        "required": true,
        "schema": {
          "type": "string"
        },
        "example": "8bdacc33-6eef-4577-beb0-633c86259f5b"
      },
      "EnvironmentId": {
        "name": "environmentId",
        "in": "path",
        "description": "ID of the environment.",
        "required": true,
        "schema": {
          "type": "string"
        },
        "example": "5v3fb205-d8a2-920f-8013-8c0e814777b3"
      },
      "PlayerId": {
        "name": "playerId",
        "in": "path",
        "description": "The player ID supplied by the Authorization service.",
        "required": true,
        "schema": {
          "type": "string"
        },
        "example": "s1QFdyYFXCC2YBocplKoLstxvQ2r"
      },
      "Key": {
        "name": "key",
        "in": "path",
        "description": "Item key.",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/Key"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad Request. Returned code indicates one of:\n- Request parameter validation failure\n- Other\nSee the errors field for more details.\n",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/BasicErrorResponse",
              "oneOf": [
                {
                  "$ref": "#/components/schemas/BasicErrorResponse"
                },
                {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              ],
              "discriminator": {
                "propertyName": "type",
                "mapping": {
                  "problems/basic": "#/components/schemas/BasicErrorResponse",
                  "problems/validation": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Invalid Authentication Token",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/BasicErrorResponse"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Forbidden",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/BasicErrorResponse"
            }
          }
        }
      },
      "NotFound": {
        "description": "Not Found",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/BasicErrorResponse"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Too Many Requests",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "number"
            },
            "description": "The number of seconds until a request will be accepted"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/BasicErrorResponse"
            }
          }
        }
      },
      "InternalServerError": {
        "description": "Internal Server Error",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/BasicErrorResponse"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Service Unavailable",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/BasicErrorResponse"
            }
          }
        }
      },
      "Conflict": {
        "description": "Conflict",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/BasicErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "BasicErrorResponse": {
        "title": "Basic error response",
        "description": "Referenced from - https://tools.ietf.org/html/rfc7807#page-3",
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "A URI reference [RFC3986] that identifies the problem type. This\nspecification encourages that, when dereferenced, it provide\nhuman-readable documentation for the problem type (e.g., using HTML\n[W3C.REC-html5-20141028]). When this member is not present, its\nvalue is assumed to be \"about:blank\".\n\n[RFC3986]: https://www.rfc-editor.org/rfc/rfc3986\n[W3C.REC-html5-20141028]: https://www.rfc-editor.org/rfc/rfc7807#ref-W3C.REC-html5-20141028\n"
          },
          "title": {
            "type": "string",
            "description": "A short, human-readable summary of the problem type. It SHOULD NOT\nchange from occurrence to occurrence of the problem, except for\npurposes of localization (e.g., using proactive content negotiation;\nsee [RFC7231, Section 3.4]).\n\n[RFC7231, Section 3.4]: https://www.rfc-editor.org/rfc/rfc7231#section-3.4\n"
          },
          "status": {
            "type": "integer",
            "description": "The HTTP status code ([RFC7231, Section 6]) generated by the origin\nserver for this occurrence of the problem.\n\n[RFC7231, Section 6]: https://www.rfc-editor.org/rfc/rfc7231#section-6\n"
          },
          "requestId": {
            "$ref": "#/components/schemas/RequestId"
          },
          "code": {
            "description": "Service specific error code",
            "type": "integer"
          },
          "detail": {
            "type": "string",
            "description": "A human-readable explanation specific to this occurrence of the problem."
          },
          "instance": {
            "type": "string",
            "nullable": true,
            "description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced."
          },
          "details": {
            "type": "array",
            "description": "Machine readable service specific errors.",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "ValidationErrorResponse": {
        "title": "ValidationErrorResponse",
        "type": "object",
        "description": "Validation error response when a value provided from the client does not pass validation on server.",
        "example": {
          "type": "problems/validation",
          "title": "validation error",
          "status": 400,
          "detail": "See 'errors' for specific validation errors",
          "code": 1009,
          "errors": [
            {
              "field": "name",
              "messages": [
                "Only alphanumeric characters allowed"
              ]
            }
          ]
        },
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "code": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationErrorBody"
            }
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "code",
          "detail",
          "errors"
        ]
      },
      "ValidationErrorBody": {
        "title": "ValidationErrorBody",
        "type": "object",
        "properties": {
          "field": {
            "type": "string"
          },
          "messages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "field",
          "messages"
        ],
        "description": "Single error in the Validation Error Response."
      },
      "PlayerId": {
        "title": "Player ID",
        "description": "The player ID supplied by the Authorization service.",
        "type": "string",
        "example": "s1QFdyYFXCC2YBocplKoLstxvQ2r"
      },
      "FileItem": {
        "title": "FileItem",
        "type": "object",
        "description": "Response type for a file stored in the Cloud Save service.",
        "properties": {
          "key": {
            "$ref": "#/components/schemas/Key"
          },
          "writeLock": {
            "$ref": "#/components/schemas/WriteLock"
          },
          "modified": {
            "$ref": "#/components/schemas/ModifiedMetadata"
          },
          "created": {
            "$ref": "#/components/schemas/ModifiedMetadata"
          },
          "contentType": {
            "type": "string",
            "description": "The MIME type of the stored file",
            "example": "text/plain"
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "description": "The size of the stored file in bytes",
            "example": 2048
          }
        },
        "required": [
          "key",
          "writeLock",
          "modified",
          "created",
          "contentType",
          "size"
        ]
      },
      "PlayerWithFilesMetadata": {
        "title": "PlayerWithFilesMetadata",
        "type": "object",
        "description": "Response type for a file stored in the Cloud Save service.",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/PlayerId"
          },
          "accessClasses": {
            "$ref": "#/components/schemas/FilesAccessClassesWithMetadata"
          }
        },
        "required": [
          "id",
          "accessClasses"
        ]
      },
      "GetPlayersWithFilesResponse": {
        "title": "GetPlayersWithFilesResponse",
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlayerWithFilesMetadata"
            },
            "description": "The array of returned player with files."
          },
          "links": {
            "type": "object",
            "properties": {
              "next": {
                "type": "string",
                "nullable": true
              }
            },
            "description": "The URL to the next page of results"
          }
        }
      },
      "FileList": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FileItem"
            },
            "description": "The array of returned file items"
          },
          "links": {
            "type": "object",
            "properties": {
              "next": {
                "type": "string",
                "nullable": true
              }
            },
            "description": "Links object containing the link for the next page of results"
          }
        }
      },
      "FileDetail": {
        "type": "object",
        "properties": {
          "signedUrl": {
            "type": "string"
          },
          "httpMethod": {
            "type": "string"
          },
          "requiredHeaders": {
            "type": "object"
          }
        }
      },
      "FilesAccessClassesWithMetadata": {
        "title": "FilesAccessClassesWithMetadata",
        "description": "The access classes for the player ID. Any given player ID can have files stored across multiple access classes.",
        "type": "object",
        "properties": {
          "default": {
            "$ref": "#/components/schemas/FilesAccessClassMetadata"
          }
        }
      },
      "FilesAccessClassMetadata": {
        "title": "FilesAccessClassMetadata",
        "description": "Files Metadata regarding a player in an access class",
        "type": "object",
        "properties": {
          "totalSize": {
            "type": "integer",
            "format": "int64",
            "description": "The total size of the files stored in bytes for this access class"
          },
          "numFiles": {
            "type": "integer",
            "description": "The number of files stored for this access class"
          }
        },
        "required": [
          "totalSize",
          "numFiles"
        ]
      },
      "Key": {
        "title": "Key",
        "type": "string",
        "pattern": "^[A-Za-z0-9_-][A-Za-z0-9_.-]{0,254}$",
        "description": "File key. Alphanumeric characters, underscores, dashes and periods; must not start with a period.",
        "minLength": 1,
        "maxLength": 255,
        "example": "save-slot-1.dat"
      },
      "WriteLock": {
        "title": "WriteLock",
        "type": "string",
        "description": "Enforces conflict checking when updating an existing file. This field should be omitted when creating a new file.\nWhen updating an existing file, omitting this field ignores write conflicts. When present, an error response will be returned if the writeLock in the request does not match the stored writeLock.\nThe writeLock is an opaque value: send back the value returned by this API without changing or interpreting it.",
        "example": "12345678901234567890"
      },
      "ModifiedMetadata": {
        "title": "ModifiedMetadata",
        "type": "object",
        "description": "Timestamp for when the object was modified.",
        "properties": {
          "date": {
            "$ref": "#/components/schemas/IsoDate"
          }
        },
        "required": [
          "date"
        ]
      },
      "IsoDate": {
        "type": "string",
        "nullable": true,
        "format": "date-time",
        "description": "Date time in ISO 8601 format. Null if there is no associated value.",
        "example": "2020-04-01T13:07:23Z"
      },
      "RequestId": {
        "title": "Request ID",
        "description": "An identifier Unity uses internally to investigate what happened to\na particular request that went through the Unity Services or Game API\nGateway.\n"
      }
    }
  }
}
```