> ## Documentation Index
> Fetch the complete documentation index at: https://plain-mattvagni-patch-1.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Attachments

> How to upload attachments programmatically for messages and events in Plain.

This page outlines how to upload attachments programmatically.

At a high level to upload attachments you:

* Make an API call to get an an upload url and some metadata
* You then upload your file, and metadata to that upload url.
* Use the ID of the attachment you uploaded in other API calls (e.g. create a thread or send an email).

## Step by step guide

To try this, you will need an [API key](/api-reference/graphql/authentication/) with the following permission:

* `attachment:create`

<Steps>
  <Step title="Creating an upload url">
    - `fileName` is the name under which the attachment will appear in the timeline, you can use whichever you want
    - `fileSizeBytes` is the exact size of the attachment in bytes (specifically, `32318` bytes is the size of Bruce's picture above)
    - `c_XXXXXXXXXXXXXXXXXXXXXXXXXX` is the customer id you are uploading the attachment for. **Remember to replace this!**

    <Tabs>
      <Tab title="Typescript SDK">
        <Snippet file="typescript-sdk/create-attachment-url.mdx" />

        Which would console log something like this:

        <Snippet file="typescript-sdk/create-attachment-url-response.mdx" />
      </Tab>

      <Tab title="GraphQL">
        The GraphQL mutation to create an attachment upload URL is the following:

        <Snippet file="graphql/create-attachment-url.mdx" />
      </Tab>
    </Tabs>
  </Step>

  <Step title="Uploading the attachment">
    In the `AttachmentUploadUrl` we created in the previous step we get back 2 fields which are needed to actually upload our attachment:

    * `uploadFormUrl`: The URL to which to upload the file to
    * `uploadFormData`: A list of key, value pairs that have to be included in the data we upload along with the actual file data.

    With this information we can now upload our actual file to Plain. To do this we need to build a form (`multipart/form-data`) with the data contained in `uploadFormData` and submit it to the `uploadFormUrl`.

    Here is some example code showing how you would do this in the Browser and from a Node server:

    <Tabs>
      <Tab title="Browser">
        <Snippet file="attachments/upload-attachment-browser.mdx" />
      </Tab>

      <Tab title="Node">
        <Snippet file="attachments/upload-attachment-node.mdx" />
      </Tab>
    </Tabs>
  </Step>
</Steps>

## Limitations

* On emails and custom timeline entries:
  * A maximum of **25 attachments** can be added
  * All attachments **combined** can not exceed **6MB** in size
* The following file extensions are not allowed as attachments:
  `
  bat, bin, chm, com, cpl, crt, exe, hlp, hta, inf, ins, isp, jse, lnk, mdb, msc, msi, msp, mst, pcd, pif, reg, scr, sct, shs, vba, vbe, vbs, wsf, wsh, wsl`
* Attachments uploaded, but never referenced by a Custom Timeline Entry or email, will be
  **deleted after 24 hours**.
* Upload URLs are only **valid for 2 hours** after which a new URL needs to be created.
