A microservice component that manages messages (activities) in Microsoft Teams:
- Send new messages
- Update existing messages
- Delete messages
Shares it's database with bf-directline-endpoint.
Uses one of two methods:
- Password-based:
MICROSOFT_APP_PASSWORD - Certificate-based: Both
MICROSOFT_APP_CERTIFICATE+MICROSOFT_APP_PRIVATEKEY
Environment variables (can be set via .env file):
PORT: Server port (default: 3980)
MICROSOFT_APP_ID: Application ID from app registrationMICROSOFT_APP_TENANT_ID: Azure AD Tenant IDMICROSOFT_APP_PASSWORD: App secret/passwordMICROSOFT_APP_CERTIFICATE: PEM certificate (Base64 encoded)MICROSOFT_APP_PRIVATEKEY: PEM private key (Base64 encoded)
TEAMS_SERVICE_URL: optional. When set, every Teams call goes to this service URL, whatever the conversation's own region says — use it to force a region for a test, or to roll back to a previous one without a deploy (for examplehttps://smba.trafficmanager.net/amer/). Leave it unset in normal operation: each conversation is then addressed at theserviceUrlTeams sent when the bot joined it, as Microsoft requires, falling back to the documented global endpointhttps://smba.trafficmanager.net/teams/only for a conversation that has none stored. Whatever its source, a URL is only used if it ishttpson one of Microsoft's documented Teams hosts (smba.trafficmanager.netand the GCC / GCC High / DoDsmba.infra.*hosts); anything else is refused with a warning and the global endpoint is used instead, since the bot's bearer token travels with every call.
DATABASE_URL: PostgreSQL connection string Format:postgresql://{USER}:{PASSWORD}@{HOST}/{DATABASE}
Interactive documentation available at:
/docs- Swagger UI/redoc- ReDoc UI
All POST routes require a conversation_token (obtained from MS Teams bot interaction) and returns a message_id:
{
"message_id": "uuid"
}The server generates a v7 UUID unless the caller supplied its own id (see below), in which case the answer echoes that one back.
- Text Message
POST /api/v1/message/text
{
"conversation_token": "conversation_token (uuid)",
"text": "Your message content"
}- Simple Message
POST /api/v1/message/simple
{
"conversation_token": "conversation_token (uuid)",
"text": "Your message content",
"title": "Message title",
"title_color": "default" // Options: dark, light, accent, good, warning, attention
}- Simple Message
POST /api/v1/message/card
{
"conversation_token": "conversation_token (uuid)",
"card": {}, // Teams Adaptive Card object
"summary": "Notification summary"
}- A generic one that can take any of the previous payload
POST /api/v1/message
{
"conversation_token": "conversation_token (uuid)",
[... one of the previous payload...]
}POST /api/v1/message also accepts an optional message_id, which must be a random UUID:
{
"conversation_token": "conversation_token (uuid)",
"message_id": "random uuid",
[... one of the previous payload...]
}A caller that writes the id down before sending keeps a usable handle even when it never sees the answer, so a timed out call no longer strands a message it can neither update nor delete.
Replaying the same message_id returns 200 with that id and sends nothing. Replaying one that
belongs to another conversation returns 409: an id may only be reused inside the conversation
that owns it, otherwise a caller could squat an id and have it handed to someone else. Replaying
the id of a deleted message returns 410, since the handle it would give back is already spent.
Concurrent replays of one id are serialised, so only one of them reaches Teams.
To update a message, send a PATCH /api/v1/message with one of the previous payload (not necessarily of the same kind), without the conversation_token but the provided message_id.
Just send a DELETE /api/v1/message without the conversation_token but the provided message_id.