> For the complete documentation index, see [llms.txt](https://docs.glue.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.glue.ai/developers/graphql-api/attachments.md).

# Attachments

This document describes how to upload attachments using the Glue API. The process involves three main steps:

1. **Get an upload ticket** - Request a signed URL for uploading
2. **Upload the file** - Upload the file content to the signed URL
3. **Create a file object** - Register the uploaded file in the system
4. **Use the file** - Attach the file to messages or other objects

## Overview

Files in Glue are uploaded directly to cloud storage using presigned URLs. The process ensures that only authorized users can upload files and that file integrity is maintained.

## API Endpoints

### 1. Get Upload Ticket

**Mutation:** `uploadTicket`

Request a signed URL for uploading a file. This endpoint validates file metadata and returns either an existing file (if the same content was already uploaded) or a new upload ticket.

```graphql
mutation UploadTicket($input: UploadTicketInput!) {
  uploadTicket(input: $input) {
    ... on File {
      id
      name
    }
    ... on UploadTicket {
      id
      url
      headers {
        name
        value
      }
      formData {
        name
        value
      }
    }
  }
}
```

**Input Parameters:**

| Field           | Type           | Required | Description                                   |
| --------------- | -------------- | -------- | --------------------------------------------- |
| `name`          | String         | Yes      | The filename                                  |
| `contentLength` | String         | Yes      | File size in bytes                            |
| `contentMD5`    | String         | Yes      | Base64-encoded MD5 hash of the file content   |
| `contentType`   | String         | Yes      | MIME type of the file                         |
| `metadata`      | UploadMetadata | No       | Additional metadata (width/height for images) |

**UploadMetadata:**

```graphql
input UploadMetadata {
  width: Int
  height: Int
}
```

**Response Types:**

The `uploadTicket` mutation returns a union type `UploadTicketResponse` that can be either:

#### Existing File Response

If a file with the same MD5 hash already exists in the system, the API returns a complete `File` object. In this case there is no need to re-upload the file and the file ID can be used immediately as an attachment ID for sending a message.

```graphql
{
  "uploadTicket": {
    "__typename": "File",
    "id": "fil_1h3c43PfmSQpPb0CXwRJUYs0ker",
    "name": "document.pdf",
  }
}
```

#### New Upload Ticket Response

If the file content is new, the API returns an `UploadTicket` object with upload instructions:

```graphql
{
  "uploadTicket": {
    "__typename": "UploadTicket",
    "id": "upl_2k4m56QgnTRqQc1DXxSKVZt1ler",
    "url": "https://storage.glue.com/upload/presigned-url",
    "headers": [
      { "name": "Content-Type", "value": "application/pdf" },
      { "name": "x-amz-acl", "value": "private" }
    ],
    "formData": null
  }
}
```

### 2. Upload File Content

Once you have an `UploadTicket`, upload the file content to the provided URL. If FormData is absent then you will nee to perform a PUT request to the specified URL. If FormData is present, then you will need to make a `multipart/form-data` POST request to the specified URL. In both cases you will need to supply the headers returned in the `UploadTicket`.

**For PUT requests (no FormData):**

```javascript
// Upload using PUT
await fetch(uploadTicket.url, {
  method: "PUT",
  headers: uploadTicket.headers.reduce((acc, header) => {
    acc[header.name] = header.value;
    return acc;
  }, {}),
  body: fileBuffer,
});
```

**For POST requests (when formData is provided):**

The `FormData` array may contain multiple entries that should be used to supply the form data for the POST. One entry will contain a value of `@file` which needs to be substituted for for the actual file content. The Content-Type will be `multipart/form-data` in order to accommodate the file data. Read more about multipart form data [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/POST#multipart_form_submission).

```javascript
// Upload using POST with form data
const formData = new FormData();
uploadTicket.formData.forEach(({ name, value }) => {
  if (value === "@file") {
    formData.append(name, fileBuffer, {
      contentType: contentType,
      filename: filename,
    });
  } else {
    formData.append(name, value);
  }
});

await fetch(uploadTicket.url, {
  method: "POST",
  headers: uploadTicket.headers.reduce((acc, header) => {
    acc[header.name] = header.value;
    return acc;
  }, {}),
  body: formData,
});
```

### 3. Create File Object

After successful upload, create a file object in the system.

**Mutation:** `createFile`

```graphql
mutation CreateFile($input: CreateFileInput!) {
  createFile(input: $input) {
    id
    name
  }
}
```

**Input Parameters:**

| Field            | Type   | Required | Description                   |
| ---------------- | ------ | -------- | ----------------------------- |
| `uploadTicketID` | String | Yes      | The ID from the upload ticket |

### File Metadata

For images and videos, provide the following metadata which will be used to the client to show an appropriately size preview of the file before it is loaded.

```graphql
type FileMetadata {
  width: Int # Image/video width in pixels
  height: Int # Image/video height in pixels
  blurHash: String # Blur hash for image previews
}
```

**Key Points:**

* **Always check `__typename`** - The response can be either `File` or `UploadTicket`
* **Existing files are immediate** - No upload step needed, file is ready to use
* **New files require upload** - Must upload content then call `createFile`

## Using Files in Messages

Once you have a file object, you can attach it to messages:

```graphql
mutation SendMessage($input: MessageInput!) {
  sendMessage(input: $input) {
    id
    text
    attachments {
      ... on File {
        id
        name
        url
        fileType
        contentType
      }
    }
  }
}
```

**Message Input:**

```graphql
input MessageInput {
  text: String!
  attachments: [ID!]! # Array of file IDs
  quotedMessageID: ID
}
```

## File Object Properties

```graphql
type File implements Node {
  id: ID! # Unique file identifier
  fileType: FileType! # Type classification
  name: String! # Original filename
  contentLength: String! # File size in bytes
  contentMD5: String! # MD5 hash for integrity
  contentType: String! # MIME type
  metadata: FileMetadata # Additional metadata
  previewable: Boolean # Whether file can be previewed
  url: String! # Download URL
}
```
