Resource
SMS
Send outbound and read inbound messages. We route through your configured provider (Telnyx / Signal House) and meter FUEL automatically.
One-off vs broadcast
For 1:1 sends (replies, transactional, post-event), use these endpoints. For list-based blasts (promos, win-backs, drips), use SMS Broadcasts — built-in scheduling, recurrence, approval gating, and per-recipient delivery state.
Bidirectional from one surface
Outbound: POST
Inbound: subscribe to
/v1/sms/messages.Inbound: subscribe to
sms.received via /v1/webhooks — we POST every incoming message to your URL within seconds.Send a message#
Send outbound SMS
POST
/v1/sms/messagesReturns
202 Accepted with a message id and conversation id once the provider has accepted the send. We log a crm_activities row of type sms so it shows up in the contact's timeline. sms.sent webhook fires when the carrier confirms delivery; sms.delivery_failed on rejection.Body parameters
tostringrequiredE.164 phone number (e.g. +15551234567).
bodystringrequiredMessage text. Max 1600 characters.
contact_iduuidOptional. Links the activity row + sets the contact context.
company_iduuidOptional. For B2B threads associated to a company.
deal_iduuidOptional. Pin the message to a specific deal's timeline.
media_urlsstring[]MMS — provider-dependent. Each URL must be HTTPS and publicly fetchable for ~24h.
Request
curl -X POST https://agency.gary.club/api/public/v1/sms/messages \
-H "Authorization: Bearer gc_live_EXAMPLE_AbCdEfGhIj0123456789" \
-H "Content-Type: application/json" \
-d '{
"to": "+15551234567",
"body": "Hi Jane — confirming Tuesday at 2pm.",
"contact_id": "con_EXAMPLE_aaaa"
}'FUEL deduction is real-time
On a successful send, your org is charged the per-message rate from your provider's pricing sheet. Out of FUEL? You'll get
402 fuel_exhausted with required + balance in details. Top up at agency.gary.club/dashboard/billing.List conversations#
List threads
GET
/v1/sms/conversationsOne row per phone-number ↔ tenant pairing. Sorted by last_message_at desc.
Query parameters
limitintegerdefault: 50page_tokenstringstatusstringFilter by conversation status (e.g.
open).phonestringSubstring match on phone_number.
Request
curl "https://agency.gary.club/api/public/v1/sms/conversations?limit=20&status=open" \
-H "Authorization: Bearer gc_live_EXAMPLE_AbCdEfGhIj0123456789"Read messages in a thread#
List messages within a thread
GET
/v1/sms/conversations/{id}/messagesCursor-paginated; newest first.
Path parameters
iduuidrequiredConversation id.
Query parameters
limitintegerdefault: 50page_tokenstringRequest
curl https://agency.gary.club/api/public/v1/sms/conversations/sc_EXAMPLE_aaaa/messages \
-H "Authorization: Bearer gc_live_EXAMPLE_AbCdEfGhIj0123456789"Webhooks for SMS#
sms.received— inbound message landed.sms.sent— your outbound was delivered by the carrier.sms.delivery_failed— carrier rejected, opt-out, or DLR fail.
The reply autopilot pattern
Subscribe to
sms.received, route the inbound through Claude in n8n, and call back to POST /v1/sms/messages to send the drafted reply. Confirm before sending if you don't fully trust the model.
