The ContextSMS Teams Platform API connects Microsoft Teams with external messaging systems. It allows teams to send and receive text messages directly through Microsoft Teams, with full support for multiple customer organizations and CSP white-labeling.
- Production base URL:
https://context-sms.sabrhub.com/api/v2 - Auth: Bearer JWT include
Authorization: Bearer <ACCESS_TOKEN>in all protected requests. - Date format:
createdDate/updateDateare epoch milliseconds (int64) unless otherwise noted. - Phone numbers: Use E.164 format (e.g.
+15551234567). - Content-Type:
application/jsonfor JSON bodies.
- Sabrhub production account with Teams Platform access.
- A CSP ID (if you are a CSP partner) — required for creating enterprises.
- Production Teams identifiers (teamId/channelId) if mapping to Teams channels.
- A secure place to store tokens (secrets manager / environment variables).
- Postman collection & Redoc URL (see References).
Follow these steps to go from zero to a working phone→Teams mapping in production.
Step A Login and obtain access token
TOKEN=$(curl -s -X POST "https://context-sms.sabrhub.com/api/v2/auth/login" -H "Content-Type: application/json" -d '{"username":"you@company.com","password":"YourStrongPassword!"}' | jq -r '.accessToken')Step B Create enterprise under a CSP (replace CSP0000003 with your CSP ID)
Take note of returned enterpriseId (e.g. E0000090).
curl -s -X POST "https://context-sms.sabrhub.com/api/v2/enterprise/CSP0000003" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
"name":"Sunshine Dental Clinic",
"contact":"Dr. Maria Rodriguez",
"number":"+15559876543",
"email":"maria@sunshinedental.com"
}' | jqStep C Add mapping (replace E0000090 with the created enterpriseId)
curl -s -X POST "https://context-sms.sabrhub.com/api/v2/mapping/add/E0000090" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{
"phoneNumber":"+15559876543",
"messagingAppType":"Teams",
"messagingAppId":"teams-channel-id-12345",
"name":"FrontDesk"
}' | jqStep D Verify mapping
curl -s -X GET "https://context-sms.sabrhub.com/api/v2/numbertomessageappmapping/enterprise/E0000090" -H "Authorization: Bearer $TOKEN" | jqIf you do not have
jq, the JSON will still print;jqis only for pretty-printing.
| Field | Meaning | Required / Notes |
|---|---|---|
contact | Enterprise contact name | required for POST /enterprise/{cspId} |
number | Enterprise phone number (E.164) | required for POST /enterprise/{cspId} |
email | Enterprise admin email | required for POST /enterprise/{cspId} |
phoneNumber | Phone number to map (E.164) | required for mapping endpoints |
messagingAppType | Messaging platform type (e.g., Teams) | required |
messagingAppId | Platform-specific identifier (e.g., Teams team/channel id) | required |
createdDate, updateDate | epoch milliseconds (int64) | convert client-side if needed |
POST /auth/login
URL: https://context-sms.sabrhub.com/api/v2/auth/login
Request
{
"username": "you@company.com",
"password": "YourStrongPassword!"
}cURL
curl -X POST "https://context-sms.sabrhub.com/api/v2/auth/login" -H "Content-Type: application/json" -d '{"username":"you@company.com","password":"YourStrongPassword!"}'JavaScript (fetch)
const res = await fetch('https://context-sms.sabrhub.com/api/v2/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'you@company.com', password: 'YourStrongPassword!' })
});
const data = await res.json();
console.log(data); // contains accessToken + refreshTokenPython (requests)
import requests
resp = requests.post(
'https://context-sms.sabrhub.com/api/v2/auth/login',
json={'username':'you@company.com','password':'YourStrongPassword!'},
headers={'Content-Type':'application/json'}
)
print(resp.json())Success (200) (example)
{
"username": "you@company.com",
"accessToken": "eyJ...ACCESS...",
"refreshToken": "eyJ...REFRESH...",
"expirationTimeInUTC": "2025-01-01T12:00:00Z",
"roles": ["CSP_BUSINESS"],
"tenantId": "YourTenant",
"services": ["Teams"]
}Notes
- Use
Authorization: Bearer <accessToken>header for protected endpoints. - Securely store
refreshTokento obtain new access tokens.
POST /auth/getaccesstoken body:
{ "username": "you@company.com", "refreshToken": "<REFRESH_TOKEN>" }(Include only if you are a CSP partner)
POST /csp https://context-sms.sabrhub.com/api/v2/csp
Request (example):
{
"email": "admin@csp.com",
"name": "Example CSP",
"carrier": "BANDWIDTH",
"houseNumber": "700 S",
"streetName": "1st ST",
"city": "Austin",
"stateCode": "TX",
"zip": "78704",
"country": "USA",
"locationId": "844849",
"subAccountId": "109733",
"authorizingPersonName": "CSP Admin"
}GET /csp/getcspconfig?phoneNumber={phoneNumber}
Example response:
{ "productName":"ContextSMS", "favicon":"favicon_url", "logo":"logo_url", "color":"#000000" }Important:
POST /enterprise/{cspId}requires the CSP ID in the path.
POST /enterprise/{cspId}
URL pattern: https://context-sms.sabrhub.com/api/v2/enterprise/{cspId}
Request (required):
{
"name": "Sunshine Dental Clinic",
"contact": "Dr. Maria Rodriguez",
"number": "+15559876543",
"email": "maria@sunshinedental.com"
}cURL
curl -X POST "https://context-sms.sabrhub.com/api/v2/enterprise/CSP0000003" -H "Authorization: Bearer YOUR_ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "name":"Sunshine Dental Clinic", "contact":"Dr. Maria Rodriguez", "number":"+15559876543", "email":"maria@sunshinedental.com" }'Success (201) example:
{
"enterpriseId": "E0000090",
"name": "Sunshine Dental Clinic",
"contact": "Dr. Maria Rodriguez",
"number": "+15559876543",
"email": "maria@sunshinedental.com",
"deleted": false,
"createdDate": 1698446777442,
"updateDate": 1698446777443
}Notes
createdDate/updateDateare epoch ms. Convert to ISO withnew Date(ms)or Python conversion shown below.
Date conversion snippets
// JS
new Date(1698446777442).toISOString()# Python
from datetime import datetime
datetime.utcfromtimestamp(1698446777442 / 1000).isoformat() + 'Z'GET /enterprise/getenterprise/{enterpriseId}
GET /csp/enterprises/{cspId}
DELETE /enterprise/deleteenterprise/{enterpriseId} returns plain text Deleted enterprise {id}
POST /mapping/add/{enterpriseId}
URL example: https://context-sms.sabrhub.com/api/v2/mapping/add/E0000027
Request (required):
{
"phoneNumber": "+12223334444",
"messagingAppType": "Teams",
"messagingAppId": "teams-channel-id-12345",
"name": "FrontDesk"
}cURL
curl -X POST "https://context-sms.sabrhub.com/api/v2/mapping/add/E0000027" -H "Authorization: Bearer YOUR_ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "phoneNumber":"+12223334444","messagingAppType":"Teams","messagingAppId":"teams-channel-id-12345","name":"FrontDesk" }'JavaScript (fetch)
const res = await fetch('https://context-sms.sabrhub.com/api/v2/mapping/add/E0000027', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
body: JSON.stringify({ phoneNumber: '+12223334444', messagingAppType: 'Teams', messagingAppId: 'teams-channel-id-12345', name: 'FrontDesk' })
});
console.log(await res.json());Python (requests)
import requests
resp = requests.post(
'https://context-sms.sabrhub.com/api/v2/mapping/add/E0000027',
json={'phoneNumber': '+12223334444', 'messagingAppType': 'Teams', 'messagingAppId': 'teams-channel-id-12345', 'name': 'FrontDesk'},
headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'}
)
print(resp.json())Success (200):
{
"numberToMessageAppMapId": "NTMAP0000123",
"phoneNumber": "+12223334444",
"messagingAppType": "Teams",
"messagingAppId": "teams-channel-id-12345",
"name": "FrontDesk"
}GET /numbertomessageappmapping/enterprise/{enterpriseId}
GET /mapping/csp/{cspId}
PUT /mapping/{numberToMessageAppMapId} partial fields allowed (example: messagingAppId, phoneNumber).
DELETE /mapping/{numberToMessageAppMapId} returns Deleted mapping {id}
POST /mapping/messagingAppId body: { "messagingAppId": "app789" } — returns mapping object if present.
POST /stripe/check-payment body: { "email": "customer@example.com" }
Response (200):
{ "email": "customer@example.com", "paid": true, "subscriptionStatus": "active" }POST /stripe/webhook accept & validate Stripe events. Example payload (minimal):
{
"id": "evt_1MqqbKLt4dXK03v5qaIbiNCC",
"object": "event",
"api_version": "2023-10-16",
"type": "customer.subscription.updated",
"data": { "object": { "id": "sub_xxx", "status": "active" } },
"livemode": false
}Success:
{ "status": "received", "processed": true }**Webhook verification **
- Enable and store your Stripe webhook signing secret securely.
- Verify the
Stripe-Signatureheader on each incoming request using Stripe SDK or HMAC SHA256 as Stripe docs instruct. - Persist processed
evt.idvalues to avoid replay. - Respond with
2xxonly after successful validation and processing.
400 Bad Request
{ "error": "Missing required field 'phoneNumber'", "code": "BAD_REQUEST" }Fix: Check required fields and JSON syntax.
401 Unauthorized
{ "error": "Invalid or expired access token", "code": "UNAUTHORIZED" }Fix: Refresh token via /auth/getaccesstoken or re-login.
403 Forbidden
{ "error": "CSP does not have permission to create enterprises", "code": "PERMISSION_DENIED" }Fix: Verify user roles and CSP privileges; contact support if needed.
404 Not Found
{ "error": "Enterprise not found", "code": "NOT_FOUND" }Fix: Confirm enterpriseId / cspId is correct and active.
409 Conflict (duplicate mapping)
{ "error": "Phone number +15551234567 is already mapped to another enterprise", "code": "DUPLICATE_MAPPING" }Fix: Query existing mappings, delete or update prior mapping before creating a new one.
500 Internal Server Error
{ "error": "Internal server error", "requestId": "req-abc123", "timestamp": "2025-10-14T12:34:56Z" }Fix: Retry with exponential backoff and send details to support including requestId.
- Full API Reference (Redoc):
https://docs.sabrhub.com - OpenAPI spec (repo):
specs/contextsms-teams.yaml - Postman collection (repo):
tests/postman-collections/teams-collection.json - Support:
support@sabrhub.cominclude full request URL, request body (no tokens), response body, and IDs (enterpriseId,numberToMessageAppMapId).