# ContextSMS Teams Platform Integration Guide 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. ## Quick facts - **Production base URL:** `https://context-sms.sabrhub.com/api/v2` - **Auth:** Bearer JWT include `Authorization: Bearer ` in all protected requests. - **Date format:** `createdDate` / `updateDate` are **epoch milliseconds (int64)** unless otherwise noted. - **Phone numbers:** Use **E.164** format (e.g. `+15551234567`). - **Content-Type:** `application/json` for JSON bodies. ## Prerequisites - 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). ## 1 Quick start zero → first mapping Follow these steps to go from zero to a working phone→Teams mapping in production. **Step A Login and obtain access token** ```bash 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`). ```bash 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" }' | jq ``` **Step C Add mapping (replace `E0000090` with the created enterpriseId)** ```bash 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" }' | jq ``` **Step D Verify mapping** ```bash curl -s -X GET "https://context-sms.sabrhub.com/api/v2/numbertomessageappmapping/enterprise/E0000090" -H "Authorization: Bearer $TOKEN" | jq ``` > If you do not have `jq`, the JSON will still print; `jq` is only for pretty-printing. ## 2 Field mapping & formats | 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 | ## 3 Authentication ### 3.1 Login obtain `accessToken` + `refreshToken` **POST** `/auth/login` **URL:** `https://context-sms.sabrhub.com/api/v2/auth/login` **Request** ```json { "username": "you@company.com", "password": "YourStrongPassword!" } ``` **cURL** ```bash 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)** ```javascript 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 + refreshToken ``` **Python (requests)** ```python 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) ```json { "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 ` header for protected endpoints. - Securely store `refreshToken` to obtain new access tokens. ### 3.2 Refresh access token **POST** `/auth/getaccesstoken` body: ```json { "username": "you@company.com", "refreshToken": "" } ``` ## 4 CSP APIs *(Include only if you are a CSP partner)* ### 4.1 Create CSP **POST** `/csp` `https://context-sms.sabrhub.com/api/v2/csp` Request (example): ```json { "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" } ``` ### 4.2 Get CSP config by phone **GET** `/csp/getcspconfig?phoneNumber={phoneNumber}` Example response: ```json { "productName":"ContextSMS", "favicon":"favicon_url", "logo":"logo_url", "color":"#000000" } ``` ## 5 Enterprise lifecycle > **Important**: `POST /enterprise/{cspId}` requires the CSP ID in the path. ### 5.1 Create enterprise **POST** `/enterprise/{cspId}` **URL pattern:** `https://context-sms.sabrhub.com/api/v2/enterprise/{cspId}` **Request (required)**: ```json { "name": "Sunshine Dental Clinic", "contact": "Dr. Maria Rodriguez", "number": "+15559876543", "email": "maria@sunshinedental.com" } ``` **cURL** ```bash 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: ```json { "enterpriseId": "E0000090", "name": "Sunshine Dental Clinic", "contact": "Dr. Maria Rodriguez", "number": "+15559876543", "email": "maria@sunshinedental.com", "deleted": false, "createdDate": 1698446777442, "updateDate": 1698446777443 } ``` **Notes** - `createdDate` / `updateDate` are epoch ms. Convert to ISO with `new Date(ms)` or Python conversion shown below. **Date conversion snippets** ```javascript // JS new Date(1698446777442).toISOString() ``` ```python # Python from datetime import datetime datetime.utcfromtimestamp(1698446777442 / 1000).isoformat() + 'Z' ``` ### 5.2 Get enterprise by ID **GET** `/enterprise/getenterprise/{enterpriseId}` ### 5.3 List enterprises for CSP **GET** `/csp/enterprises/{cspId}` ### 5.4 Delete enterprise **DELETE** `/enterprise/deleteenterprise/{enterpriseId}` returns plain text `Deleted enterprise {id}` ## 6 Mapping lifecycle ### 6.1 Add mapping (create) **POST** `/mapping/add/{enterpriseId}` **URL example:** `https://context-sms.sabrhub.com/api/v2/mapping/add/E0000027` **Request (required)**: ```json { "phoneNumber": "+12223334444", "messagingAppType": "Teams", "messagingAppId": "teams-channel-id-12345", "name": "FrontDesk" } ``` **cURL** ```bash 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)** ```javascript 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)** ```python 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)**: ```json { "numberToMessageAppMapId": "NTMAP0000123", "phoneNumber": "+12223334444", "messagingAppType": "Teams", "messagingAppId": "teams-channel-id-12345", "name": "FrontDesk" } ``` ### 6.2 Get mappings for enterprise **GET** `/numbertomessageappmapping/enterprise/{enterpriseId}` ### 6.3 Get mappings for CSP **GET** `/mapping/csp/{cspId}` ### 6.4 Update mapping **PUT** `/mapping/{numberToMessageAppMapId}` partial fields allowed (example: `messagingAppId`, `phoneNumber`). ### 6.5 Delete mapping **DELETE** `/mapping/{numberToMessageAppMapId}` returns `Deleted mapping {id}` ### 6.6 Lookup mapping by messagingAppId **POST** `/mapping/messagingAppId` body: `{ "messagingAppId": "app789" }` — returns mapping object if present. ## 7 Billing & Stripe ### 7.1 Check payment status **POST** `/stripe/check-payment` body: `{ "email": "customer@example.com" }` **Response (200)**: ```json { "email": "customer@example.com", "paid": true, "subscriptionStatus": "active" } ``` ### 7.2 Stripe webhook receiver **POST** `/stripe/webhook` accept & validate Stripe events. Example payload (minimal): ```json { "id": "evt_1MqqbKLt4dXK03v5qaIbiNCC", "object": "event", "api_version": "2023-10-16", "type": "customer.subscription.updated", "data": { "object": { "id": "sub_xxx", "status": "active" } }, "livemode": false } ``` **Success**: ```json { "status": "received", "processed": true } ``` **Webhook verification ** 1. Enable and store your Stripe webhook signing secret securely. 2. Verify the `Stripe-Signature` header on each incoming request using Stripe SDK or HMAC SHA256 as Stripe docs instruct. 3. Persist processed `evt.id` values to avoid replay. 4. Respond with `2xx` only after successful validation and processing. ## 8 Error payloads & troubleshooting **400 Bad Request** ```json { "error": "Missing required field 'phoneNumber'", "code": "BAD_REQUEST" } ``` **Fix:** Check required fields and JSON syntax. **401 Unauthorized** ```json { "error": "Invalid or expired access token", "code": "UNAUTHORIZED" } ``` **Fix:** Refresh token via `/auth/getaccesstoken` or re-login. **403 Forbidden** ```json { "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** ```json { "error": "Enterprise not found", "code": "NOT_FOUND" } ``` **Fix:** Confirm `enterpriseId` / `cspId` is correct and active. **409 Conflict (duplicate mapping)** ```json { "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** ```json { "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`. ## 9 References & support - **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.com` include full request URL, request body (no tokens), response body, and IDs (`enterpriseId`, `numberToMessageAppMapId`).