Skip to main content

Send a Chat Message

Find a chat, then send:

CHAT_ID=$(teams chat list --output json | jq -r '.data[0].id')
teams message send --chat "$CHAT_ID" --body "Status update complete." --output json

To find the chats with something new in them, order the listing by activity. Graph then sorts by each chat's newest message and includes that message as lastMessagePreview (its createdDateTime, sender, body and type). A chat's lastUpdatedDateTime is no substitute: it moves on renames and membership changes, not on new messages.

teams chat list --order-by activity --output json \
| jq -r '.data[] | [.id, .lastMessagePreview.createdDateTime, .lastMessagePreview.from.user.displayName] | @tsv'

Graph sorts this way in descending order only. A plain teams chat list carries no lastMessagePreview.

Send a pasted-style screenshot without text:

teams message send --chat "$CHAT_ID" --image ./screenshot.png --output json

Attach a file to a chat. Chat attachments upload to the sender's OneDrive Microsoft Teams Chat Files folder and require Files.ReadWrite. That folder is private to you, so after each upload the CLI grants the chat's other members read access to the file, as the Teams client does, without sending a notification email. Sharing is best-effort: if the roster lookup fails, a member has no usable address, or Graph refuses the grant, the CLI warns on stderr and asks you to share the file from OneDrive by hand, and the message still sends.

teams message send --chat "$CHAT_ID" --body "Report attached" --attach ./report.pdf --output json

Tag someone with a real @mention:

teams message send --chat "$CHAT_ID" --body "can you review?" --mention alice@contoso.com --output json

Send an Adaptive Card:

teams message send --chat "$CHAT_ID" --adaptive-card ./card.json --output json

Microsoft Graph requires the message body to reference the card, and the CLI writes that reference itself, so --body is optional with --adaptive-card. A card always sends an HTML body; a --content-type text body is escaped when it is promoted. See Reactions and Mentions for mention details.

Quote-reply​

--quote MESSAGE_ID replies with a quote, as Reply does in the Teams client: the quoted message renders as a card above the new text.

teams message send --chat "$CHAT_ID" --quote "$MESSAGE_ID" --body "Yes, that works." --output json

Repeat --quote to quote several messages of the same chat in one reply; Graph accepts up to five. It combines with --mention, --image and --attach. The CLI uses Graph's replyWithQuote action, which builds the quote card and its preview from the quoted message itself. It needs only ChatMessage.Send, which is in the default scope set. An ID that is not a message in the chat fails with NOT_FOUND (exit code 5). Quote-reply is a chat feature: in a channel, answer in the thread with teams message reply.

Delete and restore a message​

message delete removes one of your own messages. --yes is required, and without it the command exits with code 2 before sending anything. message undelete puts the message back.

teams message delete --chat "$CHAT_ID" --message "$MESSAGE_ID" --yes --output json
teams message undelete --chat "$CHAT_ID" --message "$MESSAGE_ID" --output json

This is a soft delete through Graph's softDelete and undoSoftDelete actions; Graph does not support deleting a message with the HTTP DELETE verb. Both commands read the message back: a deleted message has deletedDateTime set and an empty body, and a restored one has its text back. Deleting an already-deleted message succeeds again, so check deletedDateTime rather than the exit code to learn its state. Chat messages need Chat.ReadWrite, which is in the default scope set.

Inline images are capped at 3 MB each and 4 MB combined after base64 encoding. File attachments use Microsoft Graph's 250 MB simple-upload limit. See Message Attachments for storage, scope, and download details.

If a chat is visible but message read or send fails, check membership and meeting roster constraints.