Zendesk messaging attachments enable both agents and end users to send and receive a variety of file types during a messaging conversation. This is helpful for providing detailed support, sharing necessary documents, and improving the overall communication experience. For more information, see About private attachments in messaging in Zendesk help.

This article describes how to configure private attachments for messaging and their limitations.

Note: Beginning December 5, 2024, private attachments will be automatically available to all new customers. If you are an existing customer and wish to use private attachments in messaging, please contact your sales or customer success representative. Make sure to turn on the Secure Downloads setting. If this setting is not turned on, attachments sent by agents to end users will be public.

Disclaimer: Zendesk provides the script for illustrative purposes only. Zendesk does not support or guarantee the code in the script. Zendesk also can't provide support for third-party technologies such as Python.

Configuring SDK settings

Although there isn't a dedicated setting to enable private attachments in messaging (see About private attachments in messaging), you will need to modify certain SDK configurations based on your messaging platform.

  • You must use a version greater or equal to iOS v2.26.0 or Android v2.26.0 if you want private attachments supported in native messaging SDKs.

  • If you are using the Sunshine Conversations web messenger, you must use version 5.6.6 or later. This version supports sending credentials for private attachments.

  • If you are using Sunshine Conversations APIs, see Sunshine Conversations API considerations below.

Enabling secure downloads

To ensure the security of attachments for both end users and agents, make sure to enable the Secure Downloads setting. If this setting is not enabled, attachments sent by agents to end users will be public.

Sunshine Conversations API considerations

An API key is required to retrieve media using the Sunshine Conversations API. Below are considerations for handling private attachments within your account.

Attachment URL format

An example of an attachment URL is:

https://{subdomain}.zendesk.com/sc/attachments/v2/{attachment_id}/{filename}

This URL structure is specifically designed for accessing attachments stored within the Zendesk platform.

Embedding media

With private attachments enabled, direct embedding of media URLs into an <img> HTML tag is not supported. Instead, you must make a request to the media URL shown above, with proper credentials, to retrieve the attachment for display within your UI.

The following example provides a way to fetch and display media URLs for attachments in a React application.

Example:

// Function to fetch the media URL for a given attachmentexport const fetchMediaUrl = ({  appUserId,  appId,  clientId,  auth,  suncoUrl,  mediaUrl,}: FetchMediaUrlParams): Promise<Response> => {  // Construct the URL for the API endpoint to get the content URL  const url = `${suncoUrl}/v2/apps/${appId}/attachments/contentUrl`;
  // Create search parameters for the request, including the media URL  const searchParams = new URLSearchParams({    mediaUrl,  });
  // Make a GET request to the constructed URL with the appropriate headers  return request(`${url}?${searchParams.toString()}`, {    headers: createHeaders({ appUserId, appId, clientId, auth }),    method: 'GET',  });}
// Custom hook to manage the fetching of the content URL for media attachmentsexport const useContentUrl = ({  mediaUrl,  attachmentId,}: UseContentUrlParams): string | null => {  // Retrieve app configuration and user data from the Redux store  const { appId, suncoUrl } = useSelector(getSuncoConfig);  const { appUserId, clientId, auth } = useSelector(getUser);
  // State to hold the content URL, initialized with the provided media URL  const [contentUrl, setContentUrl] = useState<string | null>(mediaUrl);
  // Effect to fetch the content URL when dependencies change  useEffect(() => {    const fetchContentUrl = async (): Promise<void> => {      // If there's no attachment ID or the private attachment feature is disabled,      // set the content URL to the initial media URL and exit      if (!attachmentId) {        setContentUrl(mediaUrl);        return;      }
      try {        // Fetch the media URL using the fetchMediaUrl function        const response = (await fetchMediaUrl({          suncoUrl,          appUserId: appUserId || undefined,          appId,          clientId: clientId ?? '',          auth,          mediaUrl,        })) as MediaUrlResponse;
        // Update the content URL state with the fetched content URL or null if not available        setContentUrl(response?.contentUrl ?? null);      } catch (_) {        // On error, set the content URL to null        setContentUrl(null);      }    };
    // Call the fetchContentUrl function    void fetchContentUrl();  }, [mediaUrl, attachmentId, appUserId, appId, clientId, auth]);
  // Return the current content URL  return contentUrl;}
// Component to display an image structured messageconst ImageStructuredMessage = ({ message }) => {
  // Use the custom hook to get the content URL for the image  const contentUrl = useContentUrl({    mediaUrl,    attachmentId,  });
  // Render the ImageMessage component with the fetched content URL  return (    <ImageMessage      mediaUrl={contentUrl}      {...message} // Spread the message props to the ImageMessage component    />  );}

The above code snippet provides a way to fetch and display media URLs for attachments in a React application. The fetchMediaUrl function retrieves the content URL from an API, the useContentUrl hook manages the fetching logic and state, and the ImageStructuredMessage component uses the useContentUrl hook to display an image message based on the fetched content URL.

Getting the required credentials

When using private attachments, end users must authenticate themselves when they request to download an attachment. This authentication process ensures that only authorized users can access sensitive content, maintaining the integrity and confidentiality of the information.

Users can provide their identity through two primary methods: basic authentication or JSON Web Tokens (JWTs). For more information, see API Authentication.

The following overview shows the process using JWTs.

  1. Obtain an API key. You first need to acquire an API key from the Admin Center in Zendesk. This key is needed to generate the JWT and authenticate your requests.

  2. Generate the JWT. Use your platform’s library to create the JSON web token. The JWT should include the necessary claims and be signed using a secret key. For example, in Python you can use the PyJWT library to generate a JWT as follows:

    import jwt
    # Define your secret key and key IDSECRET = 'your_secret_key'  # Replace with your actual secret keyKEY_ID = 'your_key_id'       # Replace with your actual key ID
    # Create the JWT with the desired scopetoken = jwt.encode({'scope': 'app'}, SECRET, algorithm='HS256', headers={'kid': KEY_ID})

    In this example, the JWT is created with a scope of 'app', which indicates the level of access granted. The token is signed with the specified secret key and includes a header with the key ID.

  3. Make the API Call. After generating the JWT, you can use it to authenticate your API requests. Include the JWT in the request's Authorization header when making the call to download the attachment.

Understanding the different types of credentials

To download an attachment, you must include the appropriate credentials in your request. These credentials ensure that only authorized users or applications can access the resources, maintaining the security and integrity of the data. Below are example types of credentials you can use.

  • App credentials: App credentials are tied to an application and enables that application to authenticate and interact with a Sunshine Conversations API. App credentials can include API keys or JWTs.

    • JWT: When using a JWT, it is important to scope the token as an app user. This means that the token should be generated in a way that it represents an authenticated user within the context of your app.

      Setting your JWT to be app-user-scoped allows you to access user-specific resources and perform actions on behalf of that user. This is particularly important for applications that require user-specific data or actions.

    • API key: Another method for authenticating API requests is to use an API key. This is a simple string that you include in your request headers to identify your application to the Zendesk API.

  • Integration credentials: Integrations with Zendesk may require specific credentials to authenticate API requests. This can include integration tokens, used to authenticate requests made by third-party applications or services. They provide a way to securely connect your integration with Zendesk without exposing user credentials.

How requests for private attachments are evaluated

The following conditions outline how requests for private attachments are evaluated based on the credentials and attachment scope provided:

  • If a JWT with an app user scope or basic authentication credentials are supplied but the attachment is associated with a conversation that the app user is not involved in, the request is denied.

  • If the attachment is associated with a conversation that the app user is part of, the request is approved and processed successfully.

  • If the JWT for an app user or basic authentication credentials are presented, and the attachment is associated with an app that the app user does not have access to, the request is rejected.

  • If the attachment is linked to an app that the app user has access to, the request proceeds successfully.

  • If credentials for an app-scoped JWT or basic authentication are used but the attachment belongs to an app different from the one specified in the credentials, the request is denied.

  • If the provided credentials are correct but there is a mismatch between the domain of the media URL and the origin, it results in a Cross-Origin Resource Sharing (CORS) issue.

Handling CORS issues

If you encounter a CORS issue, you should route the request through your backend server, which is not subject to CORS restrictions. On your backend, make a request to the Sunshine Conversations API using the URL specified in Attachment URL format above. Once you receive the response, forward it to your Web Widget so that the image can be loaded within an <img> HTML tag

For example:

  • Set up a proxy on your server: Configure your server to act as an intermediary for requests to the external API. This approach involves setting up an endpoint on your server that handles incoming requests, processes them as needed, and forwards them to the Sunshine Conversations API.

  • Server-side request: Once the request reaches your server, your server should then make the request to the Sunshine Conversations API on behalf of the client. This step is important because your server is not subject to the same CORS restrictions imposed on browsers, allowing it to freely communicate with other servers regardless of their domain.

  • Handling the response: After your server has successfully retrieved the media from the Sunshine Conversations API, it should receive a response containing the media file or data needed. Your server then needs to relay this response back to the client's web browser. Note that the response is a 302 redirect. Meaning, you will need to return the res.headers.location header to your client's web browser.

  • Loading the media on the client side: Upon receiving the media data from your server, the client’s web application can then safely load and display the media. For images, this often involves dynamically setting the source of an <img> tag to include the data received. Typically, this is done using JavaScript to update the src attribute of the img element to reflect the new media URL provided by the server.

  • Security considerations: Ensure that any proxy setup on your server is secure. Validate all incoming requests to your proxy endpoint to avoid unauthorized usage, which could lead to security vulnerabilities or resource exploitation. Monitor and possibly rate-limit requests to your proxy to prevent abuse and ensure fair usage among all users.

Downloading attachments

If the correct credentials are provided and the domain of the URL matches your domain, you will successfully download the attachment.

You can use the Attachments endpoint to securely download an attachment. This endpoint requires SDK API authentication to access. Upon successful authentication, the endpoint will return a JSON object containing {contentUrl: <contentUrl>}. Users can then embed this contentUrl within an HTML image tag, <img src="contentUrl" />, to display the image securely in their applications. This method ensures that attachments are accessed securely and integrated smoothly into user interfaces.

Limitations of private attachments in messaging

Social channels

Attachments transmitted through third-party channels are delivered using a JWT in a public URL, with no available option to send them as private attachments. The JWTs automatically expire after 30 days, making attachment retrieval impossible after that period.

Continuous conversations

When continuous conversations is enabled, an end user who exits an ongoing conversation on a website will receive an email if an agent replies. This allows the end user to easily locate and resume the conversation at their convenience, either by responding to the email directly or returning to the web widget.

However, if the agent’s response includes an attachment, the email will only contain the message and not the attachment.

If the end user opts to respond through email and not the widget, any attachments sent through the email will not sync with the web widget. While links to attachments included in the email may sync, the actual attachments will not.

Email transcripts

Sunshine Conversation attachments are shared as links within the conversation records included in email transcripts. When using private attachments, authentication will be necessary to access these attachments from conversations. The ability to authenticate and open these attachments may vary based on the device used by the end user. There may be situations where end users cannot authenticate. For example, if a transcript of a web widget conversation was sent to the end user's email, attempts to open the attachment from the email transcript will not succeed. This is because Zendesk cannot verify that the end user is the same user from the original web widget conversation.

Third-party bots and BYOC

Before implementing third-party bots or using Bring Your Own Chat (BYOC) solutions, verify that your vendor has completed all the necessary steps outlined in this article to enable private attachments.