# 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
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.glue.ai/developers/graphql-api/attachments.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
