Mental model
Cisco objective 3.9.b says “Manage spaces, participants, and messages in Webex”. Three endpoints, same shape every time.
- A space (room) is a chat group.
- A membership ties a person to a space.
- A message is any post in a space (or a direct message to a person).
Everything you do with Webex boils down to CRUD on these three resources.
Credentials
For your own account (quick start):
- developer.webex.com → log in with your Webex account.
- Click your avatar → Copy the “Personal Access Token” (good for 12 hours).
- Export it:
export WEBEX_TOKEN="eyJhbGci...".
For a bot (long-lived):
- developer.webex.com → My Webex Apps → Create a Bot.
- Pick a name and avatar.
- Copy the bot token (does not expire).
- Any space you want the bot in: invite its email (
<name>@webex.bot).
Base URL and auth
curl -H "Authorization: Bearer $WEBEX_TOKEN" \
https://webexapis.com/v1/people/me
Returns your (or the bot’s) profile. Good first call to prove auth works.
List your spaces
curl -H "Authorization: Bearer $WEBEX_TOKEN" \
https://webexapis.com/v1/rooms
Returns an array of items, each with id, title, type (group or direct), lastActivity, etc.
List members of a space
curl -H "Authorization: Bearer $WEBEX_TOKEN" \
"https://webexapis.com/v1/memberships?roomId=Y2lzY29zcGFyazov..."
Returns everyone in the space with their personId and personEmail.
Send a message
curl -X POST \
-H "Authorization: Bearer $WEBEX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"roomId":"Y2lzY29zcGFyazov...","text":"hello from the API"}' \
https://webexapis.com/v1/messages
Alternative: send to a person by email (toPersonEmail) instead of roomId. Markdown via markdown field. Attach a file via files (URL).
Python with the SDK
from webexteamssdk import WebexTeamsAPI
api = WebexTeamsAPI(access_token="your-token-here")
me = api.people.me()
print(me.displayName)
for room in api.rooms.list(max=5):
print(room.title)
api.messages.create(toPersonEmail="you@example.com", text="hello")
The SDK handles pagination (.list() is a generator), retries on 429, and converts dicts to attribute access.
Python with requests only
import os, requests
TOKEN = os.environ["WEBEX_TOKEN"]
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
r = requests.get("https://webexapis.com/v1/people/me", headers=HEADERS)
r.raise_for_status()
print(r.json()["displayName"])
r = requests.post(
"https://webexapis.com/v1/messages",
headers=HEADERS,
json={"toPersonEmail": "you@example.com", "text": "hello"},
)
r.raise_for_status()
Making a bot that replies
# Flow:
# 1. Register a webhook: POST /webhooks { resource: 'messages', event: 'created', targetUrl: '...' }
# 2. Receive a webhook POST on your public URL when any message arrives.
# 3. The webhook payload contains the message id (not the text itself, for privacy).
# 4. GET /messages/{id} with the bot token to fetch the actual text.
# 5. POST /messages back to the roomId.
See the webhooks topic for the full receive-side pattern.
Rate limiting
Varies by endpoint. On 429, honour the Retry-After header. The SDK does this for you.
FAQ
Why does my message come back without the text in a webhook? Webex webhook payloads intentionally exclude message bodies for privacy and bandwidth. Fetch the full message with GET /messages/{id} using the bot’s token.
Can my bot see every message in a space? By default, no. Bots see messages when @-mentioned OR in a one-on-one direct message. For passive logging, admins can enable Compliance Officer access, which is a separate role.
Why do URLs end in long gibberish like Y2lzY29zcGFyazov...? Webex uses base64-encoded identifiers for globally-unique IDs. Treat them as opaque strings; do not try to parse.
Where do I pick a roomId from if my space has no API-created rooms? GET /rooms lists every room you are in with their IDs.
