Appearance
API Overview & Conventions
The Chatter API is hosted by us. It has two parts:
| Part | URL | Used for |
|---|---|---|
| REST | https://api.chatter.maragato.ca/v1 | Sign-up, profile, friends, history, uploads, all writes that are not real-time |
| WebSocket | wss://api.chatter.maragato.ca/v1/ws | Sending messages, receiving events in real time, receipts, typing, presence |
The complete REST contract is in the API Reference (download openapi.yaml to generate a client). The WebSocket protocol is in Real-time.
Workspaces and credentials
Your invitation email contains two values:
| Credential | Format | Where it goes |
|---|---|---|
clientKey | ck_… | In your app. Sent on every request. Identifies your workspace. Not secret. |
clientSecret | cs_… | Never in your app. Only for the developer endpoints (APNs setup, test users, reset), from your own machine. |
A workspace is your private copy of Chatter. All users, friendships, conversations, and messages created with your clientKey exist only in your workspace. When we test your app, we create our users in your workspace too.
Required headers
| Header | Required on | Example |
|---|---|---|
X-Client-Key | Every request except /healthz | X-Client-Key: ck_4f9a2c… |
Authorization | Every request except /auth/*, /usernames/*, /healthz, /developer/* | Authorization: Bearer eyJhbGciOi… |
X-Client-Secret | /developer/* only | X-Client-Secret: cs_8b1d… |
Content-Type | Requests with a body | application/json |
Idempotency-Key | Optional, on POST that creates resources | Idempotency-Key: 6f1c2c1e-… |
Requests without a valid X-Client-Key fail with 401 CLIENT_KEY_INVALID.
Conventions
- Format: JSON, UTF-8, field names in
camelCase. Unknown fields may be added at any time — ignore them. - IDs: ULID strings, 26 characters, for example
01JB8Z3K6Q4M2X7YV9R0T5N1PA. - Timestamps: ISO 8601 UTC with milliseconds, for example
2026-10-22T14:03:11.482Z. - Phone numbers: E.164, for example
+6580000001. - Usernames: 3–20 characters, lowercase letters, digits, and
_, starting with a letter. Unique per workspace. Case-insensitive.
Pagination
Lists use cursors:
json
{
"data": [ … ],
"page": { "nextCursor": "eyJzIjo0MjB9", "hasMore": true }
}Pass ?cursor=<nextCursor> for the next page. limit defaults to 50, maximum 100. Message history uses beforeSeq / afterSeq instead — see Messages.
Idempotency
- Messages: every send has a
clientMessageId(UUID v4) that you generate. Sending the sameclientMessageIdagain returns the original message. It never creates a duplicate. - Other creates: add
Idempotency-Key: <uuid>. A repeated key within 24 hours returns the original response.
Tokens
| Token | Lifetime | Notes |
|---|---|---|
| Access token (JWT) | 15 minutes | Send as Authorization: Bearer …. |
| Refresh token (opaque) | 30 days | Single use. Every refresh returns a new refresh token. Reusing an old one logs the device out. |
Store both in the Keychain. See Authentication.
Environments
There is one environment for the challenge. It never sends SMS: the verification code is always 000000.
Health
bash
curl https://api.chatter.maragato.ca/v1/healthz
# {"status":"ok","version":"1.0.0"}Versioning
Breaking changes go to /v2. Within /v1 we only add fields, endpoints, and event types. Your app must ignore unknown fields and unknown WebSocket event types.
Where to go next
- Quickstart: the whole test scenario with
curl, in 5 minutes. - Authentication & Profile
- Friends
- Conversations & Messages
- Real-time (WebSocket)
- Errors & Rate Limits