Developer guide

Keys

Each system has its own key, sent as Authorization: Bearer …. Test keys (tb_test_…) send only to the test recipients; live keys (tb_live_…) send to anyone in WRLD's Microsoft 365.

Send a message

POST /api/v1/messages with to.person (a work email) and either text (Teams markdown) or card (an Adaptive Card, version 1.6 or lower). Derek puts your system's name above it. Every POST needs an Idempotency-Key: repeat a request with the same key and nothing is sent twice.

curl -X POST https://teams.wchi.app/api/v1/messages \
  -H "Authorization: Bearer tb_test_…" \
  -H "Idempotency-Key: po-2026-114-approval" \
  -H "Content-Type: application/json" \
  -d '{"to": {"person": "jv@wrldcapitalholdings.com"},
       "text": "Approval needed: **PO-2026-114**"}'
{
  "message": {
    "id": "6f1c…",
    "status": "sent",
    "mode": "test",
    "to": { "person": "jv@wrldcapitalholdings.com" },
    "created_at": "2026-11-11T03:10:00.000Z",
    "status_at": "2026-11-11T03:10:01.000Z"
  }
}

Check a message

GET /api/v1/messages/{id}. The full description is at /api/v1/openapi.json.

Errors

CodeHTTPMeaning
invalid_key401The API key is missing, revoked or not recognised.
idempotency_key_required400Every POST needs an Idempotency-Key header (1 to 255 characters).
invalid_request400The request body doesn't match the API.
recipient_not_found400No one in WRLD's Microsoft 365 has that address.
test_recipient_not_allowed403A test key sends only to the test recipients.
not_found404No such message for this integration.
request_in_progress409A request with this Idempotency-Key is still being sent. Retry shortly.
idempotency_key_reused422This Idempotency-Key was already used with a different request.
rate_limited429Too many messages from this integration. Retry after the time given.
teams_rejected502Teams refused the message.
teams_unavailable502Teams or Microsoft Graph didn't answer. Retry with the same Idempotency-Key.
sending_paused503Sending is paused.
teams_app_not_ready503Derek isn't in WRLD's Teams app catalog yet.
internal500Something went wrong on Derek's side.