REST API v1
Read and manage your ChatGenius data: contacts, conversations, messages, tags, appointments, and your team. One base URL, one API key, one envelope.
Webhook & API Add-on · $29/month · Requires Creator+ planAuthentication
Every request is authenticated with your API key from the portal's Webhook & Send API page. The same key opens the REST API, the Send API, and webhooks. Send it as any one of three headers; X-SumGenius-Api-Key is preferred.
The API is server-to-server. CORS is intentionally disabled, so call it from your backend and keep the key out of browsers.
-
401 unauthorized
No API key sent.
-
401 invalid_api_key
Key not recognized.
-
403 addon_inactive
Key is valid but the add-on is not active.
https://sumgenius.ai/api/v1
curl https://sumgenius.ai/api/v1/account \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
Envelope, errors & pagination
The HTTP status carries success or failure. A single resource arrives as {"data": {...}}; a list adds has_more and next_cursor. Errors carry a stable code to branch on; message is human-facing and may change.
Lists are newest-first and cursor-paginated: pass limit (1 to 100, default 25) and feed next_cursor back as starting_after until has_more is false. Tags and team return their whole set in one page.
| HTTP | code | When |
|---|---|---|
| 400 | invalid_param | A parameter failed validation; param names it |
| 400 | invalid_json | Body is not valid JSON |
| 404 | not_found | Missing, or not owned by your account |
| 405 | Wrong method; the Allow header lists valid ones | |
| 500 | server_error | Unexpected failure on our side |
{
"data": [ ... ],
"has_more": true,
"next_cursor": "18son403"
}
{
"error": {
"code": "invalid_param",
"message": "stage must be one of: new, contacted, qualified, customer, lost.",
"param": "stage"
}
}
Data conventions
-
timestampsUTC ISO-8601
Everywhere, e.g. 2026-07-25T04:35:23+00:00. The updated_since filters accept the same format.
-
channelenum
facebook · instagram · sms · telegram · whatsapp
-
stageenum
new · contacted · qualified · customer · lost
-
statusenum
active · resolved · pending · archived · escalated (conversations)
-
custom_fieldsreserved
Always {} for now and not writable. Do not build against it until custom fields ship.
// A contact is keyed by its natural identity:
(channel, platform_user_id)
// e.g. an Instagram user:
("instagram", "17845629031224067")
Account
GET /account
Your account identity, plan, and configured webhook endpoints. A good first call to verify a key works.
curl https://sumgenius.ai/api/v1/account \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
{
"data": {
"account": {
"id": 1042,
"business_name": "Riverside Dental",
"plan": "Professional",
"addon": "webhook_api"
},
"webhooks": {
"endpoints": [
{
"id": 7,
"name": "Production endpoint",
"url": "https://api.example.com/webhooks",
"enabled": true,
"event_filters": ["message.received"],
"envelope_style": "standard"
}
]
}
}
}
Contacts
A contact is a person, keyed by (channel, platform_user_id). Contacts are created automatically when someone messages you, or explicitly via the API.
GET /contacts
List contacts, newest first. Filters combine with AND. An invalid filter value returns 400 invalid_param.
Query parameters
-
channelenumoptional
-
stageenumoptional
-
tagstringoptional
Exact tag name; contacts carrying that tag.
-
emailstringoptional
Exact match.
-
phonestringoptional
Exact match.
-
namestringoptional
Substring match on the display name.
-
updated_sincetimestampoptional
Contacts updated at or after this time.
-
limit · starting_afterpaginationoptional
curl "https://sumgenius.ai/api/v1/contacts?stage=qualified&limit=25" \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
{
"data": [
{
"id": 19214,
"channel": "instagram",
"platform_user_id": "17845629031224067",
"name": "Riley Chen",
"email": "riley@example.com",
"phone": null,
"ig_username": "rileychen",
"profile_pic_url": null,
"stage": "qualified",
"tags": ["VIP"],
"lead_score": 30,
"lead_temperature": "warm",
"notes": null,
"custom_fields": {},
"created_at": "2026-07-25T04:35:23+00:00",
"updated_at": "2026-07-26T08:05:04+00:00"
}
],
"has_more": false,
"next_cursor": null
}
GET /contacts/{id}
One contact by id, in the same shape as the list. A non-numeric, unknown, or unowned id returns 404.
curl https://sumgenius.ai/api/v1/contacts/19214 \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
POST /contacts
Create or update a contact by its natural identity. A new identity returns 201; an existing one is updated and returns 200. Updates are partial: omitted fields are untouched and existing tags are kept (tags adds, it does not replace).
Body
-
channelenumrequired
-
platform_user_idstringrequired
The person's id on that channel, 1 to 255 characters. For SMS this is the phone number.
-
namestringoptional
Up to 255 characters.
-
emailstringoptional
Valid email address.
-
phonestringoptional
Digits and + ( ) . - # x, 3 to 50 characters.
-
stageenumoptional
-
notesstringoptional
Up to 5,000 characters.
-
tagsstring[]optional
Tag names; missing tags are created automatically.
curl -X POST https://sumgenius.ai/api/v1/contacts \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"channel": "sms",
"platform_user_id": "+17025550100",
"name": "Riley Chen",
"stage": "new",
"tags": ["Imported"]
}'
{
"data": {
"id": 19301,
"channel": "sms",
"platform_user_id": "+17025550100",
"name": "Riley Chen",
"stage": "new",
"tags": ["Imported"],
...
}
}
PATCH /contacts/{id}
Partial update by id. Only fields present in the body change. Send "" to clear a field. Idempotent by nature, so no idempotency key is needed. An empty body returns 400 listing the updatable fields.
Body · all optional
-
stageenum
-
namestring
Up to 255 characters, or "" to clear.
-
emailstring
Valid email, or "" to clear.
-
phonestring
3 to 50 characters, or "" to clear.
-
notesstring
Up to 5,000 characters, or "" to clear.
curl -X PATCH https://sumgenius.ai/api/v1/contacts/19214 \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"stage": "customer",
"notes": "Paid annual plan on Jul 29"
}'
{
"data": {
"id": 19214,
"stage": "customer",
"notes": "Paid annual plan on Jul 29",
...
}
}
POST /contacts/{id}/tags
Attach a tag by name. A tag that does not exist yet is created, then attached. Re-attaching is a no-op 200. Returns the updated contact.
Body
-
namestringrequired
1 to 50 characters. Created if missing, with the default color.
curl -X POST https://sumgenius.ai/api/v1/contacts/19214/tags \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"name": "VIP"}'
DELETE /contacts/{id}/tags/{tag}
Detach a tag from a contact. {tag} is a tag id or a URL-encoded name; a name that is all digits or contains / must go by id. Detaching a tag the contact does not have is an idempotent 200; a tag that does not exist at all is 404. Only the attachment is removed; the tag itself survives.
curl -X DELETE https://sumgenius.ai/api/v1/contacts/19214/tags/VIP \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
DELETE /contacts/{id}
Permanently erases a person: the contact and every conversation, message, and appointment they have. Built for right-to-erasure requests.
This cannot be undone. To remove one thread and keep the person, use delete conversation instead.
Tag definitions and send-history audit records survive. Repeating the call is a safe 404.
curl -X DELETE https://sumgenius.ai/api/v1/contacts/19214 \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
{
"data": {
"id": 19214,
"object": "contact",
"deleted": true
}
}
Conversations
One thread per person per channel. Deleted conversations are never returned.
GET /conversations
List conversations, newest first. contact_id links each thread to its person.
Query parameters
-
channelenumoptional
-
statusenumoptional
-
assigned_tointegeroptional
A team member id from team.
-
updated_sincetimestampoptional
-
limit · starting_afterpaginationoptional
assigned_to id space: positive is a team member (assigned_to_name resolves); negative is an owner-level assignment (assigned_to_name is null); null is unassigned.
curl "https://sumgenius.ai/api/v1/conversations?status=escalated" \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
{
"data": [
{
"id": 188892,
"channel": "instagram",
"platform_user_id": "17845629031224067",
"customer_name": "Riley",
"status": "escalated",
"assigned_to": 90011,
"assigned_to_name": "Sam Ortiz",
"contact_id": 19214,
"lead_email": "riley@example.com",
"lead_phone": null,
"last_message_at": "2026-07-26T08:05:04+00:00",
"created_at": "2026-07-25T04:35:23+00:00",
"updated_at": "2026-07-26T13:00:54+00:00"
}
],
"has_more": false,
"next_cursor": null
}
POST /conversations/{id}/assign
Assign or unassign a conversation, exactly like the inbox does it: a pending or escalated thread becomes active, history is recorded, and a system line is added to the transcript. Returns the updated conversation.
Body
-
assigned_tointeger | nullrequired
A team member id from team, or null to unassign. An unknown id is 400.
curl -X POST https://sumgenius.ai/api/v1/conversations/188892/assign \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"assigned_to": 90011}'
DELETE /conversations/{id}
Permanently deletes one conversation: the full transcript, in-chat appointments, tag labels, and assignment records.
This cannot be undone. The contact survives; to erase the person entirely use delete contact.
The response counts what was removed. Analytics rows are kept but stripped of message text and unlinked; send-history audit survives. A repeat delete is a safe 404.
curl -X DELETE https://sumgenius.ai/api/v1/conversations/188892 \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
{
"data": {
"id": 188892,
"object": "conversation",
"deleted": true,
"removed": {
"messages": 15,
"appointments": 1,
"conversation_tags": 2,
"contexts": 1,
"metrics_detached": 5
}
}
}
Messages
GET /conversations/{id}/messages
The transcript of one conversation, newest first (default limit 50). Copilot drafts are excluded; this is the conversation as the customer saw it.
-
directionderived
user is inbound; bot, agent, followup are outbound; the rest is system.
-
typeenum
text · image · video · audio · file · quick_reply · button · postback · comment_trigger
-
body_purgedboolean
When a body was purged for compliance, text is null and this is true; the message is still listed.
curl "https://sumgenius.ai/api/v1/conversations/188892/messages" \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
{
"data": [
{
"id": 56698,
"message_id": "usr_6a65bfb099523",
"direction": "inbound",
"sender_type": "user",
"sender_name": null,
"type": "text",
"text": "Do you have anything open Friday?",
"attachments": [],
"is_template": false,
"body_purged": false,
"created_at": "2026-07-26T08:05:04+00:00"
}
],
"has_more": true,
"next_cursor": "56610"
}
POST /messages
Send on any channel. Same engine as the Send API with full parity; recommended for new integrations. Route by conversation_id, by platform + platform_user_id, or by to_phone for SMS and WhatsApp.
Body
-
idempotency_keystringrequired
Your unique key per send. A replay returns the original result without resending. Also accepted as an Idempotency-Key header.
-
actionenumoptional
send_message (default) · react · unreact · typing_on. Support varies by channel.
-
conversation_idinteger
Route by an existing thread.
-
platform + platform_user_idstring
Route by the person's channel id.
-
to_phonestring
Route by phone (SMS, WhatsApp).
-
message_textstringrequired
Not required for typing_on or WhatsApp template sends (template_name, template_language, template_components).
Cold outbound
SMS, WhatsApp, and Telegram may send to a recipient with no existing conversation; the thread is created automatically. Facebook and Instagram require the customer to message first.
A failed cold send still creates the conversation and contact before the delivery attempt. Repeats reuse the same records; remove them with delete contact if unwanted.
Send errors
| HTTP | code | When |
|---|---|---|
| 400 | invalid_request | Missing or invalid field |
| 403 | forbidden | Channel not permitted |
| 404 | not_found | No conversation for the routing given |
| 410 | recipient_opted_out | Recipient opted out (SMS STOP) |
| 412 | not_provisioned | Channel not connected |
| 422 | unprocessable_send | Permanent rejection: unsupported action, expired window, invalid recipient |
| 429 | rate_limited | Too many sends; retry later |
| 503 | service_unavailable | Temporary failure; may queue for retry |
curl -X POST https://sumgenius.ai/api/v1/messages \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"idempotency_key": "order-4821-confirm",
"conversation_id": 188892,
"message_text": "Your order shipped today."
}'
{
"data": {
"status": "sent",
"request_id": "msgreq_c8d83cf861ec1f48",
"conversation_id": 188892,
"platform": "instagram",
"action": "send_message",
"sent_at": "2026-07-30T06:14:18+00:00",
"message": "Message sent successfully."
}
}
{
"error": {
"code": "unprocessable_send",
"message": "action 'react' is not supported for platform 'telegram'.",
"status": "dead_letter"
}
}
Tags
Tag definitions live account-wide; attaching them to contacts is a contact operation.
GET /tags
All tag definitions, ordered by name, in one page. name is what the contacts ?tag= filter matches.
curl https://sumgenius.ai/api/v1/tags \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
{
"data": [
{
"id": 14,
"name": "Booking",
"color": "#DD6B20",
"created_at": "2026-03-06T19:55:17+00:00"
}
],
"has_more": false,
"next_cursor": null
}
POST /tags
Create a tag definition. Idempotent on name: new returns 201, existing returns 200 with its color unchanged.
Body
-
namestringrequired
1 to 50 characters, unique per account.
-
colorstringoptional
6-digit hex like #FF0000; anything else is 400.
curl -X POST https://sumgenius.ai/api/v1/tags \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"name": "VIP", "color": "#FF0000"}'
DELETE /tags/{id}
Deletes a tag definition account-wide and removes it from every contact and conversation carrying it. By numeric id only.
curl -X DELETE https://sumgenius.ai/api/v1/tags/14 \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
{"data": {"id": 14, "deleted": true}}
Appointments
Bookings made through the AI booking agent. Read-only.
GET /appointments
List bookings, newest first.
Query parameters
-
statusstringoptional
Exact match, e.g. confirmed or cancelled. Free text: an unknown value returns an empty list, not 400.
-
conversation_idintegeroptional
-
limit · starting_afterpaginationoptional
datetime is UTC, booked in timezone. Convert back with the timezone field to display the time the customer chose. meeting_link and google_event_id are null without Google Calendar sync.
curl "https://sumgenius.ai/api/v1/appointments?status=confirmed" \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
{
"data": [
{
"id": 91772,
"confirmation_code": "SG-FHZS9V",
"conversation_id": 187864,
"contact_id": 16240,
"channel": "instagram",
"datetime": "2026-07-24T23:00:00+00:00",
"timezone": "America/Los_Angeles",
"duration_minutes": 60,
"service_type": "Consultation",
"cost": "0.00",
"status": "confirmed",
"meeting_link": "https://meet.google.com/abc-defg-hij",
"google_event_id": "oa9daqujesg8dktkb777ds5gs0",
"reminder_sent": false,
"location": {"city": null, "state": null},
"customer": {
"name": "Riley",
"email": "riley@example.com",
"phone": "+17025550100",
"address": null
},
"notes": "Prefers mornings",
"created_at": "2026-07-24T07:33:22+00:00"
}
],
"has_more": false,
"next_cursor": null
}
GET /appointments/{id}
One appointment by id, same shape as the list. Unknown or unowned id returns 404.
curl https://sumgenius.ai/api/v1/appointments/91772 \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
Team
GET /team
Your team members, ordered by name, in one page. A member's id is what assigned_to references and what assign accepts. Credentials and permission data are never exposed.
-
roleenum
agent · manager
-
statusenum
online · away · busy · offline
curl https://sumgenius.ai/api/v1/team \
-H "X-SumGenius-Api-Key: sgwh_your_api_key_here"
{
"data": [
{
"id": 90011,
"name": "Sam Ortiz",
"email": "sam@example.com",
"role": "manager",
"status": "online",
"avatar_url": null,
"active_conversations": 1,
"total_resolved": 0,
"created_at": "2026-07-22T06:16:06+00:00"
}
],
"has_more": false,
"next_cursor": null
}