Skip to main content

Receiving Inline Images

When a user pastes an image into the Teams compose box, the image does not arrive inside Activity.Text. Teams sends it as an attachment on the inbound message activity.

Inline image attachments are distinct from files attached through the compose box. They do not use the file.download.info content type and are not surfaced by context.Files.

Understand the attachment shape

An inbound message with an inline image typically contains two related attachments:

{
"attachments": [
{
"contentType": "image/png",
"contentUrl": "https://.../v3/attachments/{id}/views/original"
},
{
"contentType": "text/html",
"content": "<p><img src=\"https://.../objects/{id}/views/imgo\" ...></p>"
}
]
}

Use the image/* attachment as the canonical source of the image. Its contentUrl points to the downloadable image bytes.

The text/html attachment preserves layout context, including where the image appeared among the message's text and other content. Do not use the HTML attachment's <img src> URL to download the image.

Download the image

The image attachment's contentUrl is authenticated rather than public. Fetch it with the app's authenticated HTTP client:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Teams.Apps.Clients;
using Microsoft.Teams.Apps.Schema;

HttpClient imageClient = app.Services
.GetRequiredService<IHttpClientFactory>()
.CreateClient(nameof(ApiClient));

teams.OnMessage(async (context, cancellationToken) =>
{
TeamsAttachment? image = context.Activity.Attachments?.FirstOrDefault(
attachment =>
attachment.ContentType is not null
&& attachment.ContentType.Value.StartsWith("image/", StringComparison.OrdinalIgnoreCase)
&& attachment.ContentUrl is not null);

if (image?.ContentUrl is Uri contentUrl)
{
using HttpResponseMessage response = await imageClient.GetAsync(
contentUrl,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken);
response.EnsureSuccessStatusCode();

// Read the response body as raw image bytes.
byte[] bytes = await response.Content.ReadAsByteArrayAsync(cancellationToken);
string base64 = Convert.ToBase64String(bytes);

// Pass bytes or base64 to your image-processing or model client.
}
});

The example buffers the complete image in memory. Keep the byte representation when your downstream API accepts it, or convert it to base64 when required by an image-processing or model API.