Discourse Uploads API

The Uploads API from Discourse — 7 operation(s) for uploads.

OpenAPI Specification

discourse-uploads-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: Discourse API Documentation Admin Uploads API
  x-logo:
    url: https://docs.discourse.org/logo.svg
  version: latest
  description: 'This page contains the documentation on how to use Discourse through API calls.


    > Note: For any endpoints not listed you can follow the

    [reverse engineer the Discourse API](https://meta.discourse.org/t/-/20576)

    guide to figure out how to use an API endpoint.


    ### Request Content-Type


    The Content-Type for POST and PUT requests can be set to `application/x-www-form-urlencoded`,

    `multipart/form-data`, or `application/json`.


    ### Endpoint Names and Response Content-Type


    Most API endpoints provide the same content as their HTML counterparts. For example

    the URL `/categories` serves a list of categories, the `/categories.json` API provides the

    same information in JSON format.


    Instead of sending API requests to `/categories.json` you may also send them to `/categories`

    and add an `Accept: application/json` header to the request to get the JSON response.

    Sending requests with the `Accept` header is necessary if you want to use URLs

    for related endpoints returned by the API, such as pagination URLs.

    These URLs are returned without the `.json` prefix so you need to add the header in

    order to get the correct response format.


    ### Authentication


    Some endpoints do not require any authentication, pretty much anything else will

    require you to be authenticated.


    To become authenticated you will need to create an API Key from the admin panel.


    Once you have your API Key you can pass it in along with your API Username

    as an HTTP header like this:


    ```

    curl -X GET "http://127.0.0.1:3000/admin/users/list/active.json" \

    -H "Api-Key: 714552c6148e1617aeab526d0606184b94a80ec048fc09894ff1a72b740c5f19" \

    -H "Api-Username: system"

    ```


    and this is how POST requests will look:


    ```

    curl -X POST "http://127.0.0.1:3000/categories" \

    -H "Content-Type: multipart/form-data;" \

    -H "Api-Key: 714552c6148e1617aeab526d0606184b94a80ec048fc09894ff1a72b740c5f19" \

    -H "Api-Username: system" \

    -F "name=89853c20-4409-e91a-a8ea-f6cdff96aaaa" \

    -F "color=49d9e9" \

    -F "text_color=f0fcfd"

    ```


    ### Boolean values


    If an endpoint accepts a boolean be sure to specify it as a lowercase

    `true` or `false` value unless noted otherwise.

    '
  license:
    name: MIT
    url: https://docs.discourse.org/LICENSE.txt
servers:
- url: https://{defaultHost}
  variables:
    defaultHost:
      default: discourse.example.com
tags:
- name: Uploads
paths:
  /uploads.json:
    post:
      summary: Creates an upload
      tags:
      - Uploads
      operationId: createUpload
      parameters: []
      responses:
        '200':
          description: file uploaded
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  id:
                    type: integer
                  url:
                    type: string
                  original_filename:
                    type: string
                  filesize:
                    type: integer
                  width:
                    type: integer
                  height:
                    type: integer
                  thumbnail_width:
                    type: integer
                  thumbnail_height:
                    type: integer
                  extension:
                    type: string
                  short_url:
                    type: string
                  short_path:
                    type: string
                  retain_hours:
                    type:
                    - string
                    - 'null'
                  human_filesize:
                    type: string
                  dominant_color:
                    type:
                    - string
                    - 'null'
                  thumbnail:
                    type:
                    - object
                    - 'null'
                    additionalProperties: false
                    properties:
                      id:
                        type: integer
                      upload_id:
                        type: integer
                      url:
                        type: string
                      extension:
                        type: string
                      width:
                        type: integer
                      height:
                        type: integer
                      filesize:
                        type: integer
                  optimized_video:
                    type:
                    - object
                    - 'null'
                    additionalProperties: false
                    properties:
                      id:
                        type: integer
                      upload_id:
                        type: integer
                      url:
                        type: string
                      extension:
                        type: string
                      filesize:
                        type: integer
                      sha1:
                        type: string
                      original_filename:
                        type: string
                required:
                - id
                - url
                - original_filename
                - filesize
                - width
                - height
                - thumbnail_width
                - thumbnail_height
                - extension
                - short_url
                - short_path
                - retain_hours
                - human_filesize
      requestBody:
        content:
          multipart/form-data:
            schema:
              additionalProperties: false
              properties:
                type:
                  type: string
                  enum:
                  - avatar
                  - profile_background
                  - card_background
                  - custom_emoji
                  - composer
                user_id:
                  type: integer
                  description: required if uploading an avatar
                synchronous:
                  type: boolean
                  description: Use this flag to return an id and url
                file:
                  type: string
                  format: binary
              required:
              - type
  /uploads/generate-presigned-put.json:
    post:
      summary: Initiates a direct external upload
      tags:
      - Uploads
      operationId: generatePresignedPut
      description: 'Direct external uploads bypass the usual method of creating uploads

        via the POST /uploads route, and upload directly to an external provider,

        which by default is S3. This route begins the process, and will return

        a unique identifier for the external upload as well as a presigned URL

        which is where the file binary blob should be uploaded to.


        Once the upload is complete to the external service, you must call the

        POST /complete-external-upload route using the unique identifier returned

        by this route, which will create any required Upload record in the Discourse

        database and also move file from its temporary location to the final

        destination in the external storage service.


        You must have the correct permissions and CORS settings configured in your

        external provider. We support AWS S3 as the default. See:


        https://meta.discourse.org/t/-/210469#s3-multipart-direct-uploads-4.


        An external file store must be set up and `enable_direct_s3_uploads` must

        be set to true for this endpoint to function.


        '
      parameters: []
      responses:
        '200':
          description: external upload initialized
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  key:
                    type: string
                    description: 'The path of the temporary file on the external storage

                      service.'
                    example: temp/site/uploads/default/12345/67890.jpg
                  url:
                    type: string
                    description: 'A presigned PUT URL which must be used to upload

                      the file binary blob to.'
                    example: https://file-uploads.s3.us-west-2.amazonaws.com/temp/site/uploads/default/123/456.jpg?x-amz-acl=private&x-amz-meta-sha1-checksum=sha1&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AAAAus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20211221T011246Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Signature=12345678
                  signed_headers:
                    type: object
                    description: A map of headers that must be sent with the PUT request.
                    example:
                      x-amz-acl: private
                      x-amz-meta-sha1-checksum: sha1
                  unique_identifier:
                    type: string
                    description: 'A unique string that identifies the external upload.

                      This must be stored and then sent in the /complete-external-upload

                      endpoint to complete the direct upload.'
                    example: 66e86218-80d9-4bda-b4d5-2b6def968705
      requestBody:
        content:
          application/json:
            schema:
              additionalProperties: false
              properties:
                type:
                  type: string
                  enum:
                  - avatar
                  - profile_background
                  - card_background
                  - custom_emoji
                  - composer
                file_name:
                  type: string
                  example: IMG_2021.jpeg
                file_size:
                  type: integer
                  description: File size should be represented in bytes.
                  example: 4096
                metadata:
                  type: object
                  additionalProperties: false
                  properties:
                    sha1-checksum:
                      type: string
                      description: 'The SHA1 checksum of the upload binary blob. Optionally

                        be provided and serves as an additional security check when

                        later processing the file in complete-external-upload endpoint.'
              required:
              - type
              - file_name
              - file_size
  /uploads/complete-external-upload.json:
    post:
      summary: Completes a direct external upload
      tags:
      - Uploads
      operationId: completeExternalUpload
      description: 'Completes an external upload initialized with /get-presigned-put. The

        file will be moved from its temporary location in external storage to

        a final destination in the S3 bucket. An Upload record will also be

        created in the database in most cases.


        If a sha1-checksum was provided in the initial request it will also

        be compared with the uploaded file in storage to make sure the same

        file was uploaded. The file size will be compared for the same reason.


        You must have the correct permissions and CORS settings configured in your

        external provider. We support AWS S3 as the default. See:


        https://meta.discourse.org/t/-/210469#s3-multipart-direct-uploads-4.


        An external file store must be set up and `enable_direct_s3_uploads` must

        be set to true for this endpoint to function.


        '
      parameters: []
      responses:
        '200':
          description: external upload initialized
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  id:
                    type: integer
                  url:
                    type: string
                  original_filename:
                    type: string
                  filesize:
                    type: integer
                  width:
                    type: integer
                  height:
                    type: integer
                  thumbnail_width:
                    type: integer
                  thumbnail_height:
                    type: integer
                  extension:
                    type: string
                  short_url:
                    type: string
                  short_path:
                    type: string
                  retain_hours:
                    type:
                    - string
                    - 'null'
                  human_filesize:
                    type: string
                  dominant_color:
                    type:
                    - string
                    - 'null'
                  thumbnail:
                    type:
                    - object
                    - 'null'
                    additionalProperties: false
                    properties:
                      id:
                        type: integer
                      upload_id:
                        type: integer
                      url:
                        type: string
                      extension:
                        type: string
                      width:
                        type: integer
                      height:
                        type: integer
                      filesize:
                        type: integer
                  optimized_video:
                    type:
                    - object
                    - 'null'
                    additionalProperties: false
                    properties:
                      id:
                        type: integer
                      upload_id:
                        type: integer
                      url:
                        type: string
                      extension:
                        type: string
                      filesize:
                        type: integer
                      sha1:
                        type: string
                      original_filename:
                        type: string
                required:
                - id
                - url
                - original_filename
                - filesize
                - width
                - height
                - thumbnail_width
                - thumbnail_height
                - extension
                - short_url
                - short_path
                - retain_hours
                - human_filesize
      requestBody:
        content:
          application/json:
            schema:
              additionalProperties: false
              properties:
                unique_identifier:
                  type: string
                  example: 66e86218-80d9-4bda-b4d5-2b6def968705
                  description: 'The unique identifier returned in the original /generate-presigned-put

                    request.'
                for_private_message:
                  type: string
                  example: 'true'
                  description: 'Optionally set this to true if the upload is for a

                    private message.'
                for_site_setting:
                  type: string
                  example: 'true'
                  description: 'Optionally set this to true if the upload is for a

                    site setting.'
                pasted:
                  type: string
                  example: 'true'
                  description: 'Optionally set this to true if the upload was pasted

                    into the upload area. This will convert PNG files to JPEG.'
              required:
              - unique_identifier
  /uploads/create-multipart.json:
    post:
      summary: Creates a multipart external upload
      tags:
      - Uploads
      operationId: createMultipartUpload
      description: 'Creates a multipart upload in the external storage provider, storing

        a temporary reference to the external upload similar to /get-presigned-put.


        You must have the correct permissions and CORS settings configured in your

        external provider. We support AWS S3 as the default. See:


        https://meta.discourse.org/t/-/210469#s3-multipart-direct-uploads-4.


        An external file store must be set up and `enable_direct_s3_uploads` must

        be set to true for this endpoint to function.


        '
      parameters: []
      responses:
        '200':
          description: external upload initialized
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  key:
                    type: string
                    description: 'The path of the temporary file on the external storage

                      service.'
                    example: temp/site/uploads/default/12345/67890.jpg
                  external_upload_identifier:
                    type: string
                    description: 'The identifier of the multipart upload in the external

                      storage provider. This is the multipart upload_id in AWS S3.'
                    example: 84x83tmxy398t3y._Q_z8CoJYVr69bE6D7f8J6Oo0434QquLFoYdGVerWFx9X5HDEI_TP_95c34n853495x35345394.d.ghQ
                  unique_identifier:
                    type: string
                    description: 'A unique string that identifies the external upload.

                      This must be stored and then sent in the /complete-multipart

                      and /batch-presign-multipart-parts endpoints.'
                    example: 66e86218-80d9-4bda-b4d5-2b6def968705
                required:
                - external_upload_identifier
                - key
                - unique_identifier
      requestBody:
        content:
          application/json:
            schema:
              additionalProperties: false
              properties:
                upload_type:
                  type: string
                  enum:
                  - avatar
                  - profile_background
                  - card_background
                  - custom_emoji
                  - composer
                file_name:
                  type: string
                  example: IMG_2021.jpeg
                file_size:
                  type: integer
                  description: File size should be represented in bytes.
                  example: 4096
                metadata:
                  type: object
                  additionalProperties: false
                  properties:
                    sha1-checksum:
                      type: string
                      description: 'The SHA1 checksum of the upload binary blob. Optionally

                        be provided and serves as an additional security check when

                        later processing the file in complete-external-upload endpoint.'
              required:
              - upload_type
              - file_name
              - file_size
  /uploads/batch-presign-multipart-parts.json:
    post:
      summary: Generates batches of presigned URLs for multipart parts
      tags:
      - Uploads
      operationId: batchPresignMultipartParts
      description: 'Multipart uploads are uploaded in chunks or parts to individual presigned

        URLs, similar to the one generated by /generate-presigned-put. The part

        numbers provided must be between 1 and 10000. The total number of parts

        will depend on the chunk size in bytes that you intend to use to upload

        each chunk. For example a 12MB file may have 2 5MB chunks and a final

        2MB chunk, for part numbers 1, 2, and 3.


        This endpoint will return a presigned URL for each part number provided,

        which you can then use to send PUT requests for the binary chunk corresponding

        to that part. When the part is uploaded, the provider should return an

        ETag for the part, and this should be stored along with the part number,

        because this is needed to complete the multipart upload.


        You must have the correct permissions and CORS settings configured in your

        external provider. We support AWS S3 as the default. See:


        https://meta.discourse.org/t/-/210469#s3-multipart-direct-uploads-4.


        An external file store must be set up and `enable_direct_s3_uploads` must

        be set to true for this endpoint to function.


        '
      parameters: []
      responses:
        '200':
          description: external upload initialized
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  presigned_urls:
                    type: object
                    description: 'The presigned URLs for each part number, which has

                      the part numbers as keys.'
                    example:
                      '1': https://discourse-martin-uploads-test.s3.us-east-2.amazonaws.com/temp/uploads/default/123abc/123abc.jpg?partNumber=1&uploadId=123456abcd&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test&X-Amz-Date=20211222T012336Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Signature=abc123
                required:
                - presigned_urls
      requestBody:
        content:
          application/json:
            schema:
              additionalProperties: false
              properties:
                part_numbers:
                  type: array
                  description: 'The part numbers to generate the presigned URLs for,

                    must be between 1 and 10000.'
                  example:
                  - 1
                  - 2
                  - 3
                unique_identifier:
                  type: string
                  description: 'The unique identifier returned in the original /create-multipart

                    request.'
                  example: 66e86218-80d9-4bda-b4d5-2b6def968705
              required:
              - part_numbers
              - unique_identifier
  /uploads/abort-multipart.json:
    post:
      summary: Abort multipart upload
      tags:
      - Uploads
      operationId: abortMultipart
      description: 'This endpoint aborts the multipart upload initiated with /create-multipart.

        This should be used when cancelling the upload. It does not matter if parts

        were already uploaded into the external storage provider.


        You must have the correct permissions and CORS settings configured in your

        external provider. We support AWS S3 as the default. See:


        https://meta.discourse.org/t/-/210469#s3-multipart-direct-uploads-4.


        An external file store must be set up and `enable_direct_s3_uploads` must

        be set to true for this endpoint to function.


        '
      parameters: []
      responses:
        '200':
          description: external upload initialized
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  success:
                    type: string
                    example: OK
                required:
                - success
      requestBody:
        content:
          application/json:
            schema:
              additionalProperties: false
              properties:
                external_upload_identifier:
                  type: string
                  description: 'The identifier of the multipart upload in the external

                    storage provider. This is the multipart upload_id in AWS S3.'
                  example: 84x83tmxy398t3y._Q_z8CoJYVr69bE6D7f8J6Oo0434QquLFoYdGVerWFx9X5HDEI_TP_95c34n853495x35345394.d.ghQ
              required:
              - external_upload_identifier
  /uploads/complete-multipart.json:
    post:
      summary: Complete multipart upload
      tags:
      - Uploads
      operationId: completeMultipart
      description: 'Completes the multipart upload in the external store, and copies the

        file from its temporary location to its final location in the store.

        All of the parts must have been uploaded to the external storage provider.

        An Upload record will be completed in most cases once the file is copied

        to its final location.


        You must have the correct permissions and CORS settings configured in your

        external provider. We support AWS S3 as the default. See:


        https://meta.discourse.org/t/-/210469#s3-multipart-direct-uploads-4.


        An external file store must be set up and `enable_direct_s3_uploads` must

        be set to true for this endpoint to function.


        '
      parameters: []
      responses:
        '200':
          description: external upload initialized
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  id:
                    type: integer
                  url:
                    type: string
                  original_filename:
                    type: string
                  filesize:
                    type: integer
                  width:
                    type: integer
                  height:
                    type: integer
                  thumbnail_width:
                    type: integer
                  thumbnail_height:
                    type: integer
                  extension:
                    type: string
                  short_url:
                    type: string
                  short_path:
                    type: string
                  retain_hours:
                    type:
                    - string
                    - 'null'
                  human_filesize:
                    type: string
                  dominant_color:
                    type:
                    - string
                    - 'null'
                  thumbnail:
                    type:
                    - object
                    - 'null'
                    additionalProperties: false
                    properties:
                      id:
                        type: integer
                      upload_id:
                        type: integer
                      url:
                        type: string
                      extension:
                        type: string
                      width:
                        type: integer
                      height:
                        type: integer
                      filesize:
                        type: integer
                  optimized_video:
                    type:
                    - object
                    - 'null'
                    additionalProperties: false
                    properties:
                      id:
                        type: integer
                      upload_id:
                        type: integer
                      url:
                        type: string
                      extension:
                        type: string
                      filesize:
                        type: integer
                      sha1:
                        type: string
                      original_filename:
                        type: string
                required:
                - id
                - url
                - original_filename
                - filesize
                - width
                - height
                - thumbnail_width
                - thumbnail_height
                - extension
                - short_url
                - short_path
                - retain_hours
                - human_filesize
      requestBody:
        content:
          application/json:
            schema:
              additionalProperties: false
              properties:
                unique_identifier:
                  type: string
                  example: 66e86218-80d9-4bda-b4d5-2b6def968705
                  description: 'The unique identifier returned in the original /create-multipart

                    request.'
                parts:
                  type: array
                  example:
                  - part_number: 1
                    etag: 0c376dcfcc2606f4335bbc732de93344
                  - part_number: 2
                    etag: 09ert8cfcc2606f4335bbc732de91122
                  description: 'All of the part numbers and their corresponding ETags

                    that have been uploaded must be provided.'
              required:
              - unique_identifier
              - parts