Ringhum API
REST API for Ringhum. Authenticate with a workspace API key sent as a Bearer token. All timestamps are ISO 8601 in UTC; phone numbers are E.164.
Quickstart
- Create a key under Developers → API keys. Keys belong to a workspace and are shown once.
- Send it as
Authorization: Bearer ck_live_…. - List your assistants, then place a call.
curl https://ringhum.com/api/v1/assistants -H "Authorization: Bearer ck_live_…"
curl -X POST https://ringhum.com/api/v1/calls \
-H "Authorization: Bearer ck_live_…" -H "Content-Type: application/json" \
-d '{"assistant_id": 1, "to": "+15551234567"}'
MCP server
Connect Claude, Cursor, VS Code or any other client that supports the Model Context Protocol to your workspace. The AI can then look up calls and transcripts, check availability and book appointments, update assistants and their instructions, add to the knowledge base, manage contacts, orders, reservations and campaigns, and send messages: everything the API can do, 111 tools in all.
Server URL
https://ringhum.com/mcp
Transport
Streamable HTTP
Authentication
Sign in (OAuth 2.1) or API key
Connect by signing in
For Claude, ChatGPT and other apps with MCP connectors. Add the server URL as a custom connector and choose Connect: you sign in to your account, pick the workspace and whether the app may only look things up or also make changes. The app never sees your password, and you can disconnect it any time under Developers → Connected AI apps.
- Claude (claude.ai, desktop and mobile): Settings → Connectors → Add custom connector, paste the URL, then Connect.
- ChatGPT: add it as a custom connector (MCP server) in Settings → Apps & Connectors, then Connect.
- Claude Code:
claude mcp add --transport http ringhum https://ringhum.com/mcp, then run/mcpand choose Authenticate.
Only owners and admins of a workspace can connect apps. Access tokens last 8 hours and renew on their own for 90 days of use.
Connect with an API key
- Create a key under Developers → API keys. A
readkey only gets the tools that look things up; addwriteto let the AI change things. - Add the server to your client with the key as a header:
Claude Code
claude mcp add --transport http ringhum https://ringhum.com/mcp \
--header "Authorization: Bearer ck_live_…"
Cursor (~/.cursor/mcp.json), Windsurf, Claude Desktop
{
"mcpServers": {
"ringhum": {
"url": "https://ringhum.com/mcp",
"headers": { "Authorization": "Bearer ck_live_…" }
}
}
}
VS Code (.vscode/mcp.json)
{
"servers": {
"ringhum": {
"type": "http",
"url": "https://ringhum.com/mcp",
"headers": { "Authorization": "Bearer ck_live_…" }
}
}
}
A client that only runs local servers can connect through npx mcp-remote https://ringhum.com/mcp --header "Authorization: Bearer ck_live_…".
- Every tool runs the matching API endpoint with your key, so validation, scopes, plan limits, webhooks and responses are exactly those described below.
- Tools that only read are marked read-only, deletions are marked destructive, and placing calls or sending messages is marked as reaching outside your workspace, so clients can ask you before they run.
- Tool calls count toward the same 120 requests a minute as the API. Revoke the key to cut access at once.
All 111 tools
Account
who_am_iWho am Iget_workspaceGet workspacelist_membersList membersget_usageGet usage
Assistants
list_assistantsList assistantscreate_assistantwriteCreate assistantget_assistantGet assistantupdate_assistantwriteUpdate assistantdelete_assistantwriteDelete assistantlist_coaching_suggestionsList coaching suggestionsreview_recent_callswriteReview recent callsapply_suggestionwriteApply suggestiondismiss_suggestionwriteDismiss suggestionget_website_widgetGet website widgetupdate_website_widgetwriteUpdate website widgetget_customer_email_lookGet customer email lookupdate_customer_email_lookwriteUpdate customer email look
Phone numbers
list_phone_numbersList phone numbersget_whatsapp_profileGet WhatsApp profileupdate_whatsapp_profilewriteUpdate WhatsApp profileget_phone_numberGet phone numberupdate_phone_numberwriteUpdate phone number
Calls
list_callsList callsplace_outbound_callwritePlace outbound callget_callGet calldelete_callwriteDelete callget_transcriptGet transcript
Webhooks
list_webhooksList webhookscreate_webhookwriteCreate webhookupdate_webhookwriteUpdate webhookdelete_webhookwriteDelete webhooksend_test_eventwriteSend test event
Contacts
list_contactsList contactscreate_contactwriteCreate contactimport_contactswriteImport contactsask_to_call_on_whatsappwriteAsk to call on WhatsAppget_contactGet contactupdate_contactwriteUpdate contactdelete_contactwriteDelete contact
Appointments
get_appointment_availabilityGet availabilitylist_appointmentsList appointmentsbook_appointmentwriteBook appointmentget_appointmentGet appointmentupdate_appointmentwriteUpdate appointmentcancel_appointmentwriteCancel appointment
Orders
list_ordersList orderscreate_orderwriteCreate orderquote_orderQuote orderget_orderGet orderupdate_orderwriteUpdate ordercancel_orderwriteCancel order
Products
list_productsList productscreate_productwriteCreate productget_productGet productupdate_productwriteUpdate productdelete_productwriteDelete product
Reservations
get_room_availabilityGet availabilitylist_reservationsList reservationscreate_reservationwriteCreate reservationget_reservationGet reservationupdate_reservationwriteUpdate reservationcancel_reservationwriteCancel reservation
Rooms
list_roomsList roomscreate_roomwriteCreate roomget_roomGet roomupdate_roomwriteUpdate roomdelete_roomwriteDelete room
Inbox
list_inbox_itemsList inbox itemsget_inbox_itemGet inbox itemupdate_inbox_itemwriteUpdate inbox item
Messages
list_messagesList messagessend_messagewriteSend messageget_messageGet messagelist_instagram_and_messenger_channelsList Instagram and Messenger channels
Campaigns
list_campaignsList campaignscreate_campaignwriteCreate campaignget_campaignGet campaignlist_campaign_resultsList campaign resultsadd_contacts_to_campaignwriteAdd contacts to campaignstart_pause_or_cancel_campaignwriteStart, pause or cancel campaign
Company
get_company_profileGet company profileupdate_company_profilewriteUpdate company profileadd_servicewriteAdd serviceupdate_servicewriteUpdate servicedelete_servicewriteDelete serviceadd_faqwriteAdd FAQdelete_faqwriteDelete FAQadd_staff_memberwriteAdd staff memberdelete_staff_memberwriteDelete staff memberlist_knowledge_documentsList knowledge documentsadd_knowledge_documentwriteAdd knowledge documentsearch_knowledgeSearch knowledgedelete_knowledge_documentwriteDelete knowledge documentlist_questions_it_could_not_answerList questions it could not answersuggest_an_answerwriteSuggest an answeranswer_as_faqwriteAnswer as FAQdismiss_questionwriteDismiss question
Growth
get_payment_settingsGet payment settingsupdate_payment_settingswriteUpdate payment settingsget_review_request_settingsGet review request settingsupdate_review_request_settingswriteUpdate review request settingslist_review_requestsList review requestsget_revenue_from_the_assistantGet revenue from the assistant
Spam protection
get_spam_protectionGet spam protectionupdate_spam_protectionwriteUpdate spam protectionlist_blocked_numbersList blocked numbersblock_a_numberwriteBlock a numberunblock_a_numberwriteUnblock a number
Catalog
list_voicesList voiceslist_languagesList languageslist_webhook_eventsList webhook events
Authentication and scopes
Every request needs a Bearer API key. Keys are hashed at rest, can be revoked instantly and carry scopes:
read
List and get assistants, numbers, calls, transcripts, recordings, usage.
write
Create, update and delete assistants and webhooks; assign numbers; place calls; send messages.
A key without the needed scope receives 403 permission_error. Each endpoint below shows the scope it requires.
Conventions
- Base URL
https://ringhum.com/api/v1. JSON in, JSON out (Content-Type: application/json). - Single objects come back as
{"data": {…}}; lists as{"data": [...], "meta": {pagination}}. Usepageandper_page(max 100). - Timestamps are ISO 8601 in UTC. Phone numbers are E.164 (
+14155550132). Ids are integers. - Every response carries an
X-Request-Idheader; include it when contacting support. - Updates use
PATCHwith only the fields you want to change.PUTis accepted as an alias.
Errors
Failures always have the same shape, with a stable type you can switch on:
{
"error": {
"type": "validation_error",
"message": "The to field format is invalid.",
"errors": { "to": ["Use E.164 format, e.g. +15551234567."] }
}
}
| Status | type | When |
|---|---|---|
| 401 | authentication_error | Missing, revoked or unknown key |
| 403 | permission_error | Key lacks the scope |
| 404 | not_found | Resource is not in this workspace |
| 422 | validation_error | Bad body or query; see errors |
| 422 | plan_limit | Plan limit reached (assistants, numbers) |
| 429 | rate_limit_error | Too many requests; honour Retry-After |
| 502 | — | Carrier rejected an outbound call (body is the failed call) |
Rate limits
120 requests per minute per key. Responses include X-RateLimit-Limit and X-RateLimit-Remaining; when exceeded you get 429 with Retry-After in seconds. Poll GET /calls/{id} no more than once every few seconds, or use webhooks instead of polling.
Native integrations
59 connectors ship in the dashboard under Integrations. Each is either a token app (credentials pasted in a dialog and verified before saving), an OAuth app (sign-in redirect) or a webhook app (a URL you paste). Every connection chooses its events and keeps a log of deliveries.
| Category | Apps | What happens |
|---|---|---|
| Messaging & alerts | Slack, Microsoft Teams, Discord, Telegram, Google Chat, Pushover | Formatted summaries/alerts posted per event |
| Automation | Zapier, Make, n8n | Signed JSON POST per event (X-Ringhum-Event, X-Ringhum-Signature) |
| CRM & sales | HubSpot, Pipedrive, Zoho CRM, Close, GoHighLevel, Attio, Copper, Freshsales, Capsule CRM, Bitrix24 | Contact upsert + call activity/note; external id stored on the contact |
| Help desk | Zendesk, Freshdesk, Intercom, Help Scout | Ticket/conversation per inbox item (optionally per call) |
| Tasks & projects | Trello, Asana, ClickUp, monday.com, Todoist, Jira, Linear | Task/card/issue per inbox item |
| Spreadsheets & files | Notion, Airtable, Google Sheets, Google Drive, Dropbox | Row/record/page per call; files for transcripts and recordings |
| Email & marketing | Mailchimp, Brevo, ActiveCampaign, Klaviyo, Customer.io | Subscriber upsert with name, phone, outcome tag |
| Analytics | Segment, Mixpanel, PostHog, Google Analytics 4 | identify + track / capture events |
| E-commerce | Shopify, WooCommerce | Read-only order lookup tool for the assistant |
| POS, ordering & printing | HubRise, Square POS, Clover, PrintNode | |
| Hotels & booking channels | Booking.com, Airbnb, Vrbo | |
| Calendars & scheduling | Google Calendar, Calendar feed (ICS), Calendly, Cal.com, Acuity Scheduling, Square Appointments | Busy times merged into availability; Google Calendar two-way |
Events a connection can subscribe to: call.ended, inbox.created, appointment.booked, appointment.cancelled, order.created, order.cancelled. Webhook-style apps (Zapier, Make, n8n) receive {"event", "workspace", "sent_at", "data"}; when a secret is set the body is signed with X-Ringhum-Signature: sha256=<hmac>.
Assistant tools
During a call the model can call these functions. Each hits an internal endpoint scoped to the call with a per-call token, so the assistant only ever sees its own workspace.
| Tool | Used when | What it does |
|---|---|---|
| search_knowledge | Any factual question | Searches FAQs, documents and crawled pages; returns passages the model must quote from. |
| check_availability | Caller wants a time | Free slots for a service/staff member over the next days, minus every connected calendar's busy time. |
| book_appointment | Caller picks a slot | Creates the appointment, contact and confirmations; writes to Google Calendar; fires appointment.booked. |
| find_appointments / cancel_appointment | Reschedule or cancel | Looks up the caller's bookings by number and moves or cancels them with the same availability checks. |
| take_message / request_callback | A human is needed later | Creates an inbox item (message, callback, lead) with priority and notifies the team and connected apps. |
| send_sms | Caller wants something in writing | Sends the caller a short message (address, link, confirmation) on WhatsApp if the caller uses it, otherwise by text. Asks for permission first. |
| get_menu | Caller asks what you sell or orders | Reads your menu or catalogue (or the part matching what the caller asked for) with prices and options. |
| quote_order | Before confirming an order | Prices the basket from your catalogue, adds the delivery fee and lists anything to ask about (a missing size, an unknown item, the minimum order). Nothing is saved. |
| place_order | Caller confirms the order | Creates the order with server-side prices, links the contact, notifies the customer and your team, and fires order.created to webhooks and connected apps. |
| find_orders | Order or delivery question | Finds the caller's orders taken on Ringhum and in Shopify / WooCommerce by phone, email or order number, with status, items, total and tracking. |
| cancel_order | Caller wants to cancel | Cancels one of the caller's open orders, notifies the customer and team and fires order.cancelled. |
| save_details | Caller shares details | Saves name, email, company, notes and your custom variables to the contact record. |
| end_call | The conversation is done | Says goodbye and hangs up; the summary, outcome and integrations run right after. |
Orders and menus
Assistants with Take orders switched on take orders from the products under Menu & products: pizzas, groceries, flowers, parts, anything with a price. Each product can have option groups (a required size, any number of extras) with their own surcharges. Pickup, delivery or dine-in, the delivery fee, a minimum order, preparation time and whether orders are accepted outside opening hours are set per workspace.
- Prices are always worked out on the server from your catalogue. The model and API clients only say which products, how many and which options, so a total cannot be talked down.
- New orders appear on the Orders board and move through
new → confirmed → preparing → ready → out_for_delivery → completed(orcancelled). The customer gets an email (and a text where SMS is on) when the order is placed, ready or on its way. - Every order fires
order.created, status changes fireorder.updatedand cancellations fireorder.cancelled, to your webhooks and to connected apps. - Manage the catalogue with
/productsand orders with/orders.POST /orders/quoteprices a basket without saving it.
Hotels and reservations
Assistants with Take room reservations switched on check availability and book stays against the room types under Rooms: each type has a capacity, how many identical units you have, a nightly price and seasonal rates by date range. Check-in and check-out times, the minimum and maximum stay, breakfast, deposit and cancellation policies are set per workspace and read to callers.
- Availability and prices are always worked out on the server: a room type is free for a stay when fewer units than you own are held on every night, counting reservations from every source. The model only says which room, which nights and how many guests.
- New reservations appear on the Reservations page and move through
requested → confirmed → checked_in → checked_out(orcancelled/no_show). The guest gets an email (and a text where SMS is on) when the stay is booked, confirmed or cancelled. - Booking.com, Airbnb and VRBO. Paste each site's calendar export link on the room type and its bookings block the room here within the hour (they appear as reservations with source
ical). Paste the room's own export address, from Rooms → Calendar sync orical_export_urlon the room, into the site's "import calendar" so stays booked on the phone are blocked there too. - Every reservation fires
reservation.created, status changes firereservation.updatedand cancellations firereservation.cancelled. - Manage room types with
/roomsand stays with/reservations.GET /reservations/availabilityreturns the free units and the price per room type for a stay without saving anything.
A phone number in your workspace can be connected to WhatsApp Business. Once it is, people message it and call it on WhatsApp and the same assistant answers, with the same knowledge base, abilities and handover rules it uses on the phone. WhatsApp is on every plan; calls and messages come out of the same minutes as phone calls.
Enabling it
- Under Phone numbers, open the number and choose Enable WhatsApp. Give the display name people will see in WhatsApp (your business name).
- The setup is automatic and usually takes a few minutes: the number is registered with WhatsApp Business, WhatsApp texts it a verification code that is entered for you, and Meta reviews the display name. The number's status moves from
requestedtopendingtoactive. - The number keeps working for ordinary calls and texts throughout. Once active, switch AI replies and WhatsApp calling on or off per number, and edit the WhatsApp profile (photo, about line, description, category, contact details) from the same page or through the API. Saving the Company page updates the profile too.
What the assistant does
- Messages. With AI replies on, the assistant answers WhatsApp messages around the clock from the same knowledge base and with the same abilities as on a call: it answers questions, books and reschedules appointments, takes orders and takes a message when a person is needed. Every conversation lands in the Inbox with the contact, so your team can pick it up and reply from there. Photos, documents and voice notes people send are kept with the message.
- Calls. With WhatsApp calling on, a WhatsApp call to the number is answered exactly like a phone call: same assistant, same greeting, and a transcript and summary afterwards. Calls over WhatsApp carry
"channel": "whatsapp"in the API.
Messaging rules
- WhatsApp lets a business send free-form messages for 24 hours after the person's last message. Inside that window the assistant, your team and the API can reply freely; outside it a message is refused and logged as failed with the reason. The person writing to you again reopens the window.
- Starting a conversation yourself needs a Meta-approved template message. Templates are not supported yet, so today WhatsApp conversations always begin with the customer. Confirmations and reminders go on WhatsApp when the window is open and by text otherwise (where texting is on).
- Numbers do not need to be in the same country: WhatsApp messages go anywhere. Texts still only go to numbers in the sending number's country.
Calling rules
- People can call you on WhatsApp at any time, from anywhere; the assistant picks up.
- A call you start (from the dashboard or
POST /callswithchannel: whatsapp) needs the person's permission first. They grant it by accepting a call-permission request in WhatsApp, for a limited time or permanently, and it is kept on the contact aswhatsapp_call_permission. Without it the call is refused with a422. - WhatsApp does not let businesses start calls to numbers in the United States, Canada, Egypt, Vietnam or Nigeria. People there can still call you.
Pricing
- WhatsApp messages come out of your plan's minutes: each message sent or received counts as about 0.13 of a minute (its 1¢ price over your plan's per-minute rate). Past your minutes a message costs 1¢ from credit. The Billing page shows how many minutes went to WhatsApp.
- WhatsApp calls come out of your plan's minutes like phone calls, but cheaper: a WhatsApp minute counts as 0.8 of a plan minute, because there is no phone line to pay for (beyond your minutes, 0.8 × your plan's per-minute rate, or of the $0.09 credit rate without a plan). Calls people make to you cost that and nothing more; calls you start on WhatsApp add WhatsApp's per-country calling fee, for example Germany $0.024, Italy $0.028, the UK $0.023 a minute.
In the API
GET /phone-numbersreturns awhatsappobject per number:status,display_name,calling,ai_repliesandactivated_at.GET /messageslists texts and WhatsApp messages (filter withchannel=whatsapp);POST /messagessends one, withchannelauto,whatsapporsms. See Messages below.POST /callsacceptschannel: "whatsapp"; the preconditions above come back as422 validation_error.- Contacts carry
whatsapp_call_permission(permanent,until).
Webhooks
Register an HTTPS endpoint (dashboard or POST /webhooks) and choose events. Deliveries are JSON POSTs, retried 3 times with back-off (10 s, 60 s, 5 min) until your endpoint returns a 2xx.
POST https://example.com/hooks/ringhum
X-Ringhum-Event: call.ended
X-Ringhum-Timestamp: 1789250000
X-Ringhum-Signature: v1=5f1a…
{
"id": "evt_8f2k\u2026",
"type": "call.ended",
"created_at": "2026-09-13T14:02:11+00:00",
"data": {}
}
Verify the signature: HMAC-SHA256 of "{timestamp}.{raw body}" with the endpoint secret, compared in constant time. Reject timestamps older than 5 minutes to prevent replays.
// Node const sig = req.header('X-Ringhum-Signature'); const ts = req.header('X-Ringhum-Timestamp'); const expected = 'v1=' + crypto.createHmac('sha256', SECRET).update(`${ts}.${rawBody}`).digest('hex'); if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) throw new Error('bad signature');// PHP $sig = $request->header('X-Ringhum-Signature'); $ts = $request->header('X-Ringhum-Timestamp'); $expected = 'v1='.hash_hmac('sha256', $ts.'.'.$request->getContent(), $secret); abort_unless(hash_equals($expected, $sig), 400);# Python import hmac, hashlib expected = "v1=" + hmac.new(SECRET.encode(), f"{ts}.{raw_body}".encode(), hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, sig): raise ValueError("bad signature")
Event reference
call.started
A call was answered by an assistant
call.ended
A call ended (includes transcript & summary)
message.taken
The assistant took a message
appointment.booked
The assistant booked an appointment
appointment.rescheduled
An appointment was moved to a new time
appointment.cancelled
An appointment was cancelled
order.created
An order was placed (by an assistant, by hand or through the API)
order.updated
An order moved to a new status
order.cancelled
An order was cancelled
reservation.created
A room was reserved (by an assistant, by hand or through the API)
reservation.updated
A reservation moved to a new status (confirmed, checked in, checked out, no-show)
reservation.cancelled
A reservation was cancelled
campaign.completed
An outbound campaign finished calling its list
test.ping
Sent by "Send test" in the dashboard or POST /webhooks/{id}/test.
call.* payloads contain the call object as returned by GET /calls/{id} (summary, outcome, extracted variables, transcript).
Account
Who you are, your workspace and usage.
/me
— Who am I
scope: read
Returns the API key, the user who created it and the workspace it belongs to.
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/me \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/me', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/me'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/me", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "api_key",
"id": 1,
"name": "string",
"email": "string",
"scopes": [
"read"
],
"created_by": {
"id": 1,
"object": "member",
"name": "string",
"email": "string",
"role": "owner",
"joined_at": "2026-09-13T14:02:11+00:00"
},
"team": {
"id": 1,
"object": "team",
"name": "string",
"slug": "string",
"timezone": "string",
"plan": "trial",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
}
/team
— Get workspace
scope: read
The workspace this key belongs to.
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/team \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/team', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/team'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/team", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "team",
"name": "string",
"slug": "string",
"timezone": "string",
"plan": "trial",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/team/members
— List members
scope: read
Members of the workspace with their role.
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/team/members \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/team/members', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/team/members'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/team/members", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "member",
"name": "string",
"email": "string",
"role": "owner",
"joined_at": "2026-09-13T14:02:11+00:00"
}
]
}
/usage
— Get usage
scope: read
Minutes used in the current billing period, plan limits and counts.
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/usage \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/usage', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/usage'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/usage", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "usage",
"period": {
"start": "2026-09-13T14:02:11+00:00",
"end": "2026-09-13T14:02:11+00:00"
},
"plan": {
"key": "string",
"name": "string",
"included_minutes": 1,
"overage_per_minute": 1
},
"minutes_used": 1,
"minutes_remaining": 1,
"calls": 1,
"completed_calls": 1,
"assistants": 1,
"phone_numbers": 1
}
}
Assistants
AI phone assistants: voice, behaviour, abilities.
/assistants
— List assistants
scope: read
Paginated. Archived assistants are excluded unless status=archived.
Parameters
| statusquery | string: draft | active | paused | archived | |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/assistants \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/assistants', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/assistants'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/assistants", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "assistant",
"name": "Front desk",
"status": "draft",
"persona_name": "Marie",
"company_name": "string",
"language": "en-US",
"voice": {
"id": "string",
"name": "string",
"speed": 1
},
"greeting": "string",
"abilities": {
"answer_questions": true,
"take_message": true,
"book_appointments": false,
"take_orders": false
},
"phone_numbers": [
"+14155550132"
],
"record_calls": true,
"recording_notice": true,
"recording_notice_text": "string",
"calls_count": 1,
"minutes_used": 1,
"last_call_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/assistants
— Create assistant
scope: write
Creates an assistant. Only name is required; everything else falls back to the platform defaults (first voice, default abilities).
Body (AssistantInput)
| name* | string | |
| status | string | draft | active | paused |
| persona_name | string | |
| company_name | string | |
| company_description | string | |
| language | string | See GET /languages |
| voice_id | string | See GET /voices |
| speech_rate | number | |
| greeting | string | |
| instructions | string | |
| knowledge_text | string | |
| knowledge_url | string | |
| abilities | object | |
| transfer_numbers | array | array of TransferTarget |
| extract_variables | array | array of Variable |
| temperature | number | |
| max_tokens | integer | |
| allow_interruptions | boolean | |
| interruption_sensitivity | string | low | medium | high |
| max_call_duration_seconds | integer | |
| record_calls | boolean | |
| recording_notice | boolean | |
| recording_notice_text | string|null | |
| send_summary_email | boolean | |
| summary_email | string |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/assistants \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"Front desk","persona_name":"Marie","company_name":"Northside Dental","greeting":"Hi, thanks for calling Northside Dental, this is Marie. How can I help?","abilities":{"answer_questions":true,"take_message":true,"take_orders":true},"transfer_numbers":[{"label":"Accounting","number":"+14155550178","when":"billing questions"}]}'const res = await fetch('https://ringhum.com/api/v1/assistants', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "Front desk", "persona_name": "Marie", "company_name": "Northside Dental", "greeting": "Hi, thanks for calling Northside Dental, this is Marie. How can I help?", "abilities": { "answer_questions": true, "take_message": true, "take_orders": true }, "transfer_numbers": [ { "label": "Accounting", "number": "+14155550178", "when": "billing questions" } ] }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/assistants', array ( 'name' => 'Front desk', 'persona_name' => 'Marie', 'company_name' => 'Northside Dental', 'greeting' => 'Hi, thanks for calling Northside Dental, this is Marie. How can I help?', 'abilities' => array ( 'answer_questions' => true, 'take_message' => true, 'take_orders' => true, ), 'transfer_numbers' => array ( 0 => array ( 'label' => 'Accounting', 'number' => '+14155550178', 'when' => 'billing questions', ), ), )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/assistants", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"Front desk","persona_name":"Marie","company_name":"Northside Dental","greeting":"Hi, thanks for calling Northside Dental, this is Marie. How can I help?","abilities":{"answer_questions":true,"take_message":true,"take_orders":true},"transfer_numbers":[{"label":"Accounting","number":"+14155550178","when":"billing questions"}]}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "assistant",
"name": "Front desk",
"status": "draft",
"persona_name": "Marie",
"company_name": "string",
"language": "en-US",
"voice": {
"id": "string",
"name": "string",
"speed": 1
},
"greeting": "string",
"abilities": {
"answer_questions": true,
"take_message": true,
"book_appointments": false,
"take_orders": false
},
"phone_numbers": [
"+14155550132"
],
"record_calls": true,
"recording_notice": true,
"recording_notice_text": "string",
"calls_count": 1,
"minutes_used": 1,
"last_call_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00",
"company_description": "string",
"instructions": "string",
"knowledge_text": "string",
"knowledge_url": "string",
"transfer_numbers": [
{
"label": "string",
"number": "+14155550132",
"when": "string"
}
],
"extract_variables": [
{
"name": "string",
"description": "string"
}
],
"business_hours": {},
"after_hours_message": "string",
"temperature": 1,
"max_tokens": 1,
"allow_interruptions": true,
"interruption_sensitivity": "low",
"interrupt_greeting": true,
"max_call_duration_seconds": 1,
"silence_timeout_seconds": 1,
"background_sound": "string",
"send_summary_email": true,
"summary_email": "string"
}
}
/assistants/{id}
— Get assistant
scope: read
Full configuration including instructions and knowledge base.
Parameters
| idpath | integer | Assistant id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/assistants/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/assistants/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/assistants/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/assistants/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "assistant",
"name": "Front desk",
"status": "draft",
"persona_name": "Marie",
"company_name": "string",
"language": "en-US",
"voice": {
"id": "string",
"name": "string",
"speed": 1
},
"greeting": "string",
"abilities": {
"answer_questions": true,
"take_message": true,
"book_appointments": false,
"take_orders": false
},
"phone_numbers": [
"+14155550132"
],
"record_calls": true,
"recording_notice": true,
"recording_notice_text": "string",
"calls_count": 1,
"minutes_used": 1,
"last_call_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00",
"company_description": "string",
"instructions": "string",
"knowledge_text": "string",
"knowledge_url": "string",
"transfer_numbers": [
{
"label": "string",
"number": "+14155550132",
"when": "string"
}
],
"extract_variables": [
{
"name": "string",
"description": "string"
}
],
"business_hours": {},
"after_hours_message": "string",
"temperature": 1,
"max_tokens": 1,
"allow_interruptions": true,
"interruption_sensitivity": "low",
"interrupt_greeting": true,
"max_call_duration_seconds": 1,
"silence_timeout_seconds": 1,
"background_sound": "string",
"send_summary_email": true,
"summary_email": "string"
}
}
/assistants/{id}
— Update assistant
scope: write
Partial update; send only the fields you want to change.
Parameters
| idpath | integer | Assistant id (required) |
Body (AssistantInput)
| name* | string | |
| status | string | draft | active | paused |
| persona_name | string | |
| company_name | string | |
| company_description | string | |
| language | string | See GET /languages |
| voice_id | string | See GET /voices |
| speech_rate | number | |
| greeting | string | |
| instructions | string | |
| knowledge_text | string | |
| knowledge_url | string | |
| abilities | object | |
| transfer_numbers | array | array of TransferTarget |
| extract_variables | array | array of Variable |
| temperature | number | |
| max_tokens | integer | |
| allow_interruptions | boolean | |
| interruption_sensitivity | string | low | medium | high |
| max_call_duration_seconds | integer | |
| record_calls | boolean | |
| recording_notice | boolean | |
| recording_notice_text | string|null | |
| send_summary_email | boolean | |
| summary_email | string |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/assistants/1 \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"Front desk","persona_name":"Marie","company_name":"Northside Dental","greeting":"Hi, thanks for calling Northside Dental, this is Marie. How can I help?","abilities":{"answer_questions":true,"take_message":true,"take_orders":true},"transfer_numbers":[{"label":"Accounting","number":"+14155550178","when":"billing questions"}]}'const res = await fetch('https://ringhum.com/api/v1/assistants/1', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "Front desk", "persona_name": "Marie", "company_name": "Northside Dental", "greeting": "Hi, thanks for calling Northside Dental, this is Marie. How can I help?", "abilities": { "answer_questions": true, "take_message": true, "take_orders": true }, "transfer_numbers": [ { "label": "Accounting", "number": "+14155550178", "when": "billing questions" } ] }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/assistants/1', array ( 'name' => 'Front desk', 'persona_name' => 'Marie', 'company_name' => 'Northside Dental', 'greeting' => 'Hi, thanks for calling Northside Dental, this is Marie. How can I help?', 'abilities' => array ( 'answer_questions' => true, 'take_message' => true, 'take_orders' => true, ), 'transfer_numbers' => array ( 0 => array ( 'label' => 'Accounting', 'number' => '+14155550178', 'when' => 'billing questions', ), ), )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/assistants/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"Front desk","persona_name":"Marie","company_name":"Northside Dental","greeting":"Hi, thanks for calling Northside Dental, this is Marie. How can I help?","abilities":{"answer_questions":true,"take_message":true,"take_orders":true},"transfer_numbers":[{"label":"Accounting","number":"+14155550178","when":"billing questions"}]}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "assistant",
"name": "Front desk",
"status": "draft",
"persona_name": "Marie",
"company_name": "string",
"language": "en-US",
"voice": {
"id": "string",
"name": "string",
"speed": 1
},
"greeting": "string",
"abilities": {
"answer_questions": true,
"take_message": true,
"book_appointments": false,
"take_orders": false
},
"phone_numbers": [
"+14155550132"
],
"record_calls": true,
"recording_notice": true,
"recording_notice_text": "string",
"calls_count": 1,
"minutes_used": 1,
"last_call_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00",
"company_description": "string",
"instructions": "string",
"knowledge_text": "string",
"knowledge_url": "string",
"transfer_numbers": [
{
"label": "string",
"number": "+14155550132",
"when": "string"
}
],
"extract_variables": [
{
"name": "string",
"description": "string"
}
],
"business_hours": {},
"after_hours_message": "string",
"temperature": 1,
"max_tokens": 1,
"allow_interruptions": true,
"interruption_sensitivity": "low",
"interrupt_greeting": true,
"max_call_duration_seconds": 1,
"silence_timeout_seconds": 1,
"background_sound": "string",
"send_summary_email": true,
"summary_email": "string"
}
}
/assistants/{id}
— Delete assistant
scope: write
Archives and soft-deletes the assistant; its phone numbers are unassigned. Calls are kept.
Parameters
| idpath | integer | Assistant id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/assistants/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/assistants/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/assistants/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/assistants/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "string",
"deleted": true
}
}
/assistants/{id}/suggestions
— List coaching suggestions
scope: read
Changes to the instructions suggested from the assistant's reviewed calls, with its average call score.
Parameters
| idpath | integer | Assistant id (required) |
| statusquery | string: open | applied | dismissed |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/assistants/1/suggestions \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/assistants/1/suggestions', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/assistants/1/suggestions'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/assistants/1/suggestions", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "assistant_suggestion",
"assistant_id": 1,
"title": "string",
"reason": "string",
"instruction": "string",
"call_ids": [
1
],
"status": "open",
"applied_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00"
}
]
}
/assistants/{id}/suggestions/review
— Review recent calls
scope: write
Reviews the last 14 days of calls now and returns any new suggestions. Once an hour per assistant.
Parameters
| idpath | integer | Assistant id (required) |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/assistants/1/suggestions/review \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/assistants/1/suggestions/review', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/assistants/1/suggestions/review'); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/assistants/1/suggestions/review", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "assistant_suggestion",
"assistant_id": 1,
"title": "string",
"reason": "string",
"instruction": "string",
"call_ids": [
1
],
"status": "open",
"applied_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00"
}
]
}
/assistants/{id}/suggestions/{suggestion}/apply
— Apply suggestion
scope: write
Adds the suggestion's sentence to the assistant's instructions.
Parameters
| idpath | integer | Assistant id (required) |
| suggestionpath | integer | Suggestion id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/assistants/1/suggestions/1/apply \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/assistants/1/suggestions/1/apply', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/assistants/1/suggestions/1/apply'); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/assistants/1/suggestions/1/apply", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "assistant_suggestion",
"assistant_id": 1,
"title": "string",
"reason": "string",
"instruction": "string",
"call_ids": [
1
],
"status": "open",
"applied_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/assistants/{id}/suggestions/{suggestion}/dismiss
— Dismiss suggestion
scope: write
Parameters
| idpath | integer | Assistant id (required) |
| suggestionpath | integer | Suggestion id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/assistants/1/suggestions/1/dismiss \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/assistants/1/suggestions/1/dismiss', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/assistants/1/suggestions/1/dismiss'); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/assistants/1/suggestions/1/dismiss", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "assistant_suggestion",
"assistant_id": 1,
"title": "string",
"reason": "string",
"instruction": "string",
"call_ids": [
1
],
"status": "open",
"applied_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/assistants/{id}/widget
— Get website widget
scope: read
The chat and call button for the business's website: whether it is on, chat and calls, look, allowed websites and the embed code.
Parameters
| idpath | integer | Assistant id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/assistants/1/widget \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/assistants/1/widget', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/assistants/1/widget'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/assistants/1/widget", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "widget",
"assistant_id": 1,
"included_in_plan": true,
"enabled": true,
"chat": true,
"calls": true,
"label": "string",
"welcome": "string",
"color": "string",
"position": "left",
"captions": true,
"branding": true,
"allowed_websites": [
"string"
],
"embed_code": "string"
}
}
/assistants/{id}/widget
— Update website widget
scope: write
Team and Scale. Switch the widget, chat or calls on or off, change the label, welcome text, colour and position, and the websites allowed to show it. Only the fields sent change.
Parameters
| idpath | integer | Assistant id (required) |
Body (WidgetInput)
| enabled | boolean | |
| chat | boolean | |
| calls | boolean | |
| label | string | |
| welcome | string|null | |
| color | string | |
| position | string | left | right |
| captions | boolean | |
| branding | boolean | |
| allowed_websites | array |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/assistants/1/widget \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"enabled":true,"chat":true,"calls":true,"label":"string","welcome":"string","color":"string","position":"left","captions":true,"branding":true,"allowed_websites":["example.com"]}'const res = await fetch('https://ringhum.com/api/v1/assistants/1/widget', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "enabled": true, "chat": true, "calls": true, "label": "string", "welcome": "string", "color": "string", "position": "left", "captions": true, "branding": true, "allowed_websites": [ "example.com" ] }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/assistants/1/widget', array ( 'enabled' => true, 'chat' => true, 'calls' => true, 'label' => 'string', 'welcome' => 'string', 'color' => 'string', 'position' => 'left', 'captions' => true, 'branding' => true, 'allowed_websites' => array ( 0 => 'example.com', ), )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/assistants/1/widget", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"enabled":true,"chat":true,"calls":true,"label":"string","welcome":"string","color":"string","position":"left","captions":true,"branding":true,"allowed_websites":["example.com"]}, ) data = r.json()["data"]
{
"data": {
"object": "widget",
"assistant_id": 1,
"included_in_plan": true,
"enabled": true,
"chat": true,
"calls": true,
"label": "string",
"welcome": "string",
"color": "string",
"position": "left",
"captions": true,
"branding": true,
"allowed_websites": [
"string"
],
"embed_code": "string"
}
}
/assistants/{id}/email-branding
— Get customer email look
scope: read
Sender name, colour, footer, "Sent using" line and the wording of each email this assistant's customers get.
Parameters
| idpath | integer | Assistant id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/assistants/1/email-branding \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/assistants/1/email-branding', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/assistants/1/email-branding'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/assistants/1/email-branding", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "email_branding",
"assistant_id": 1,
"included_in_plan": true,
"sender_name": "string",
"accent": "#e11d48",
"footer": "string",
"powered_by": true,
"logo_url": "string",
"templates": {}
}
}
/assistants/{id}/email-branding
— Update customer email look
scope: write
Solo and up. Only the fields sent change; per email, intro and signoff (empty = standard text). The logo is uploaded in the dashboard.
Parameters
| idpath | integer | Assistant id (required) |
Body (EmailBrandingInput)
| sender_name | string|null | |
| accent | string | |
| footer | string|null | |
| powered_by | boolean | Show "Sent using Ringhum". |
| templates | object | Per email: appointment, order, reservation, callback_promise, info_request, review_request. Placeholders such as :customer and :company are filled in. |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/assistants/1/email-branding \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"sender_name":"string","accent":"string","footer":"string","powered_by":true,"templates":{}}'const res = await fetch('https://ringhum.com/api/v1/assistants/1/email-branding', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "sender_name": "string", "accent": "string", "footer": "string", "powered_by": true, "templates": {} }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/assistants/1/email-branding', array ( 'sender_name' => 'string', 'accent' => 'string', 'footer' => 'string', 'powered_by' => true, 'templates' => (object) array( ), )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/assistants/1/email-branding", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"sender_name":"string","accent":"string","footer":"string","powered_by":true,"templates":{}}, ) data = r.json()["data"]
{
"data": {
"object": "email_branding",
"assistant_id": 1,
"included_in_plan": true,
"sender_name": "string",
"accent": "#e11d48",
"footer": "string",
"powered_by": true,
"logo_url": "string",
"templates": {}
}
}
Phone numbers
Numbers your assistants answer on.
/phone-numbers
— List phone numbers
scope: read
Active numbers in the workspace.
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/phone-numbers \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/phone-numbers', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/phone-numbers'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/phone-numbers", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "phone_number",
"e164": "+14155550132",
"formatted": "string",
"friendly_name": "string",
"country": "string",
"type": "local",
"status": "string",
"assistant_id": 1,
"capabilities": {},
"monthly_cost_cents": 1,
"whatsapp": {
"status": "requested",
"display_name": "string",
"calling": true,
"ai_replies": true,
"activated_at": "2026-09-13T14:02:11+00:00"
},
"created_at": "2026-09-13T14:02:11+00:00"
}
]
}
/phone-numbers/{id}/whatsapp-profile
— Get WhatsApp profile
scope: read
What people see when they open the number in WhatsApp. 409 when WhatsApp is not active on the number.
Parameters
| idpath | integer | Phone number id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 409WhatsApp not active
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/phone-numbers/1/whatsapp-profile \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/phone-numbers/1/whatsapp-profile', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/phone-numbers/1/whatsapp-profile'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/phone-numbers/1/whatsapp-profile", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "whatsapp_profile",
"phone_number_id": 1,
"display_name": "string",
"about": "string",
"description": "string",
"category": "OTHER",
"email": "string",
"website": "string",
"address": "string",
"profile_photo_url": "string"
}
}
/phone-numbers/{id}/whatsapp-profile
— Update WhatsApp profile
scope: write
Change the display name (reviewed by Meta before it shows), about line, description, category, email, website, address, and the profile photo (photo_url or photo_base64, JPEG or PNG up to 5 MB). Only the fields sent are changed.
Parameters
| idpath | integer | Phone number id (required) |
Body (WhatsAppProfileInput)
| display_name | string | |
| about | string | |
| description | string | |
| category | string | OTHER | AUTO | BEAUTY | APPAREL | EDU | ENTERTAIN | EVENT_PLAN | FINANCE | GROCERY | GOVT | HOTEL | HEALTH | NONPROFIT | PROF_SERVICES | RETAIL | TRAVEL | RESTAURANT |
| string | ||
| website | string | |
| address | string | |
| photo_url | string | Public URL of a JPEG or PNG. |
| photo_base64 | string | The image itself, base64 (a data: URL is accepted). |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 409WhatsApp not active
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/phone-numbers/1/whatsapp-profile \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"display_name":"string","about":"string","description":"string","category":"OTHER","email":"string","website":"string","address":"string","photo_url":"string","photo_base64":"string"}'const res = await fetch('https://ringhum.com/api/v1/phone-numbers/1/whatsapp-profile', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "display_name": "string", "about": "string", "description": "string", "category": "OTHER", "email": "string", "website": "string", "address": "string", "photo_url": "string", "photo_base64": "string" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/phone-numbers/1/whatsapp-profile', array ( 'display_name' => 'string', 'about' => 'string', 'description' => 'string', 'category' => 'OTHER', 'email' => 'string', 'website' => 'string', 'address' => 'string', 'photo_url' => 'string', 'photo_base64' => 'string', )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/phone-numbers/1/whatsapp-profile", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"display_name":"string","about":"string","description":"string","category":"OTHER","email":"string","website":"string","address":"string","photo_url":"string","photo_base64":"string"}, ) data = r.json()["data"]
{
"data": {
"object": "whatsapp_profile",
"phone_number_id": 1,
"display_name": "string",
"about": "string",
"description": "string",
"category": "OTHER",
"email": "string",
"website": "string",
"address": "string",
"profile_photo_url": "string"
}
}
/phone-numbers/{id}
— Get phone number
scope: read
Parameters
| idpath | integer | Phone number id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/phone-numbers/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/phone-numbers/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/phone-numbers/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/phone-numbers/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "phone_number",
"e164": "+14155550132",
"formatted": "string",
"friendly_name": "string",
"country": "string",
"type": "local",
"status": "string",
"assistant_id": 1,
"capabilities": {},
"monthly_cost_cents": 1,
"whatsapp": {
"status": "requested",
"display_name": "string",
"calling": true,
"ai_replies": true,
"activated_at": "2026-09-13T14:02:11+00:00"
},
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/phone-numbers/{id}
— Update phone number
scope: write
Assign an assistant (assistant_id, or null to unassign) or rename it.
Parameters
| idpath | integer | Phone number id (required) |
Body (PhoneNumberInput)
| assistant_id | integer|null | |
| friendly_name | string |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/phone-numbers/1 \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"assistant_id":1,"friendly_name":"string"}'const res = await fetch('https://ringhum.com/api/v1/phone-numbers/1', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "assistant_id": 1, "friendly_name": "string" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/phone-numbers/1', array ( 'assistant_id' => 1, 'friendly_name' => 'string', )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/phone-numbers/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"assistant_id":1,"friendly_name":"string"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "phone_number",
"e164": "+14155550132",
"formatted": "string",
"friendly_name": "string",
"country": "string",
"type": "local",
"status": "string",
"assistant_id": 1,
"capabilities": {},
"monthly_cost_cents": 1,
"whatsapp": {
"status": "requested",
"display_name": "string",
"calling": true,
"ai_replies": true,
"activated_at": "2026-09-13T14:02:11+00:00"
},
"created_at": "2026-09-13T14:02:11+00:00"
}
}
Calls
Inbound and outbound calls, transcripts and recordings.
/calls
— List calls
scope: read
Newest first. Filter by assistant, status, direction, phone number and date range.
Parameters
| assistant_idquery | integer | |
| statusquery | string: queued | ringing | in_progress | completed | busy | failed | no_answer | canceled | |
| directionquery | string: inbound | outbound | |
| phonequery | string | E.164 number that called or was called. |
| fromquery | string | Started at or after. |
| toquery | string | Started at or before. |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/calls \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/calls', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/calls'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/calls", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1042,
"object": "call",
"direction": "inbound",
"channel": "phone",
"status": "queued",
"outcome": "resolved",
"sentiment": "positive",
"from": "+14155550132",
"to": "+14155550132",
"caller_name": "string",
"assistant_id": 1,
"phone_number_id": 1,
"started_at": "2026-09-13T14:02:11+00:00",
"answered_at": "2026-09-13T14:02:11+00:00",
"ended_at": "2026-09-13T14:02:11+00:00",
"duration_seconds": 1,
"billable_minutes": 1,
"summary": "Sarah called to book a cleaning; booked Thursday 2:30 PM.",
"quality_score": 1,
"quality_notes": [
"string"
],
"extracted": {},
"has_recording": true,
"is_test": true,
"error": "string",
"created_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/calls
— Place outbound call
scope: write
The assistant calls the number and opens with its welcome message. Returns 201 with status queued; follow progress with GET /calls/{id} or the call.ended webhook. Returns 502 if the carrier rejected the call. Set channel to whatsapp to call the person on WhatsApp instead of the phone network: the workspace needs a number with WhatsApp calling switched on, the person must have allowed WhatsApp calls (a permission they grant in WhatsApp, kept on the contact as whatsapp_call_permission), and WhatsApp does not let businesses start calls to numbers in the US, Canada, Egypt, Vietnam or Nigeria. Whatever is missing comes back as 422 validation_error with the reason under errors.to.
Body (OutboundCallInput)
| assistant_id* | integer | |
| to* | string | E.164 phone number |
| from_phone_number_id | integer | Optional; defaults to the assistant's number. |
| channel | string | Optional; `phone` by default. `whatsapp` calls the person on WhatsApp from a number with WhatsApp calling, and needs their permission first. phone | whatsapp |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
- 502Carrier error; `status` is `failed` and `error` explains why.
curl -X POST https://ringhum.com/api/v1/calls \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"assistant_id":1,"to":"+15551234567"}'const res = await fetch('https://ringhum.com/api/v1/calls', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "assistant_id": 1, "to": "+15551234567" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/calls', array ( 'assistant_id' => 1, 'to' => '+15551234567', )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/calls", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"assistant_id":1,"to":"+15551234567"}, ) data = r.json()["data"]
{
"data": {
"id": 1042,
"object": "call",
"direction": "inbound",
"channel": "phone",
"status": "queued",
"outcome": "resolved",
"sentiment": "positive",
"from": "+14155550132",
"to": "+14155550132",
"caller_name": "string",
"assistant_id": 1,
"phone_number_id": 1,
"started_at": "2026-09-13T14:02:11+00:00",
"answered_at": "2026-09-13T14:02:11+00:00",
"ended_at": "2026-09-13T14:02:11+00:00",
"duration_seconds": 1,
"billable_minutes": 1,
"summary": "Sarah called to book a cleaning; booked Thursday 2:30 PM.",
"quality_score": 1,
"quality_notes": [
"string"
],
"extracted": {},
"has_recording": true,
"is_test": true,
"error": "string",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/calls/{id}
— Get call
scope: read
Includes transcript, conversation metrics and metadata.
Parameters
| idpath | integer | Call id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/calls/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/calls/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/calls/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/calls/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1042,
"object": "call",
"direction": "inbound",
"channel": "phone",
"status": "queued",
"outcome": "resolved",
"sentiment": "positive",
"from": "+14155550132",
"to": "+14155550132",
"caller_name": "string",
"assistant_id": 1,
"phone_number_id": 1,
"started_at": "2026-09-13T14:02:11+00:00",
"answered_at": "2026-09-13T14:02:11+00:00",
"ended_at": "2026-09-13T14:02:11+00:00",
"duration_seconds": 1,
"billable_minutes": 1,
"summary": "Sarah called to book a cleaning; booked Thursday 2:30 PM.",
"quality_score": 1,
"quality_notes": [
"string"
],
"extracted": {},
"has_recording": true,
"is_test": true,
"error": "string",
"created_at": "2026-09-13T14:02:11+00:00",
"metrics": {},
"metadata": {},
"transcript": [
{
"role": "user",
"content": "string",
"spoken_at": "2026-09-13T14:02:11+00:00"
}
]
}
}
/calls/{id}
— Delete call
scope: write
Deletes the call, its transcript and recording.
Parameters
| idpath | integer | Call id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/calls/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/calls/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/calls/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/calls/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "string",
"deleted": true
}
}
/calls/{id}/transcript
— Get transcript
scope: read
Just the conversation turns, in order.
Parameters
| idpath | integer | Call id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/calls/1/transcript \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/calls/1/transcript', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/calls/1/transcript'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/calls/1/transcript", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"role": "user",
"content": "string",
"spoken_at": "2026-09-13T14:02:11+00:00"
}
]
}
/calls/{id}/recording
— Download recording
scope: read
Streams the call audio as audio/wav (8 kHz mono). 404 if recording was disabled for the assistant.
Parameters
| idpath | integer | Call id (required) |
Responses
- 200WAV audio
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/calls/1/recording \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/calls/1/recording', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/calls/1/recording'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/calls/1/recording", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
Webhooks
Signed HTTP callbacks for call events.
/webhooks
— List webhooks
scope: read
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/webhooks \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/webhooks', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/webhooks'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/webhooks", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "webhook",
"url": "string",
"events": [
"string"
],
"active": true,
"last_status": 1,
"last_triggered_at": "2026-09-13T14:02:11+00:00",
"failure_count": 1,
"created_at": "2026-09-13T14:02:11+00:00"
}
]
}
/webhooks
— Create webhook
scope: write
The signing secret is returned **only in this response**. Store it to verify deliveries.
Body (WebhookInput)
| url* | string | Must be https. |
| events* | array | |
| active | boolean |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/webhooks \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"url":"https://example.com/hooks/ringhum","events":["call.ended","appointment.booked"]}'const res = await fetch('https://ringhum.com/api/v1/webhooks', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "url": "https://example.com/hooks/ringhum", "events": [ "call.ended", "appointment.booked" ] }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/webhooks', array ( 'url' => 'https://example.com/hooks/ringhum', 'events' => array ( 0 => 'call.ended', 1 => 'appointment.booked', ), )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/webhooks", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"url":"https://example.com/hooks/ringhum","events":["call.ended","appointment.booked"]}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "webhook",
"url": "string",
"events": [
"string"
],
"active": true,
"last_status": 1,
"last_triggered_at": "2026-09-13T14:02:11+00:00",
"failure_count": 1,
"created_at": "2026-09-13T14:02:11+00:00",
"secret": "whsec_…"
}
}
/webhooks/{id}
— Update webhook
scope: write
Change the URL, the events or pause it with active: false.
Parameters
| idpath | integer | Webhook id (required) |
Body (WebhookInput)
| url* | string | Must be https. |
| events* | array | |
| active | boolean |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/webhooks/1 \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"url":"https://example.com/hooks/ringhum","events":["call.ended","appointment.booked"]}'const res = await fetch('https://ringhum.com/api/v1/webhooks/1', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "url": "https://example.com/hooks/ringhum", "events": [ "call.ended", "appointment.booked" ] }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/webhooks/1', array ( 'url' => 'https://example.com/hooks/ringhum', 'events' => array ( 0 => 'call.ended', 1 => 'appointment.booked', ), )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/webhooks/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"url":"https://example.com/hooks/ringhum","events":["call.ended","appointment.booked"]}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "webhook",
"url": "string",
"events": [
"string"
],
"active": true,
"last_status": 1,
"last_triggered_at": "2026-09-13T14:02:11+00:00",
"failure_count": 1,
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/webhooks/{id}
— Delete webhook
scope: write
Parameters
| idpath | integer | Webhook id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/webhooks/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/webhooks/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/webhooks/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/webhooks/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "string",
"deleted": true
}
}
/webhooks/{id}/test
— Send test event
scope: write
Delivers a test.ping event synchronously and reports the HTTP status your endpoint returned.
Parameters
| idpath | integer | Webhook id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/webhooks/1/test \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/webhooks/1/test', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/webhooks/1/test'); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/webhooks/1/test", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"delivered": true,
"status": 1
}
}
Contacts
Callers and customers, recognised on repeat calls.
/contacts
— List contacts
scope: read
Newest activity first. Filter with q (name, phone, email, company), tag, phone, email or do_not_call.
Parameters
| qquery | string | |
| tagquery | string | |
| phonequery | string | |
| do_not_callquery | boolean | |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/contacts \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/contacts', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/contacts'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/contacts", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "contact",
"name": "string",
"phone": "+14155550100",
"email": "string",
"company": "string",
"tags": [
"string"
],
"do_not_call": true,
"source": "call",
"calls_count": 1,
"last_call_at": "2026-09-13T14:02:11+00:00",
"last_appointment_at": "2026-09-13T14:02:11+00:00",
"external_ids": {},
"whatsapp_call_permission": {
"permanent": true,
"until": "2026-09-13T14:02:11+00:00"
},
"created_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/contacts
— Create contact
scope: write
Creates a contact, or updates the existing one with the same phone number.
Body (ContactInput)
| name | string | |
| phone | string | E.164 phone number |
| string | ||
| company | string | |
| tags | array | |
| notes | string | |
| custom | object | |
| do_not_call | boolean |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/contacts \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"string","phone":"+14155550132","email":"string","company":"string","tags":["string"],"notes":"string","custom":{},"do_not_call":true}'const res = await fetch('https://ringhum.com/api/v1/contacts', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "string", "phone": "+14155550132", "email": "string", "company": "string", "tags": [ "string" ], "notes": "string", "custom": {}, "do_not_call": true }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/contacts', array ( 'name' => 'string', 'phone' => '+14155550132', 'email' => 'string', 'company' => 'string', 'tags' => array ( 0 => 'string', ), 'notes' => 'string', 'custom' => (object) array( ), 'do_not_call' => true, )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/contacts", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"string","phone":"+14155550132","email":"string","company":"string","tags":["string"],"notes":"string","custom":{},"do_not_call":true}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "contact",
"name": "string",
"phone": "+14155550100",
"email": "string",
"company": "string",
"tags": [
"string"
],
"do_not_call": true,
"source": "call",
"calls_count": 1,
"last_call_at": "2026-09-13T14:02:11+00:00",
"last_appointment_at": "2026-09-13T14:02:11+00:00",
"external_ids": {},
"whatsapp_call_permission": {
"permanent": true,
"until": "2026-09-13T14:02:11+00:00"
},
"created_at": "2026-09-13T14:02:11+00:00",
"notes": "string",
"custom": {}
}
}
/contacts/import
— Import contacts
scope: write
Bulk upsert up to 5,000 rows. Recognised keys: name, phone, email, company, tags, notes; other keys become custom fields.
Body (ContactImportInput)
| contacts* | array | |
| tags | array | Added to every imported contact. |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/contacts/import \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"contacts":[{"name":"Ann Lee","phone":"415-555-0101","plan":"gold"}],"tags":["recall-2026"]}'const res = await fetch('https://ringhum.com/api/v1/contacts/import', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "contacts": [ { "name": "Ann Lee", "phone": "415-555-0101", "plan": "gold" } ], "tags": [ "recall-2026" ] }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/contacts/import', array ( 'contacts' => array ( 0 => array ( 'name' => 'Ann Lee', 'phone' => '415-555-0101', 'plan' => 'gold', ), ), 'tags' => array ( 0 => 'recall-2026', ), )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/contacts/import", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"contacts":[{"name":"Ann Lee","phone":"415-555-0101","plan":"gold"}],"tags":["recall-2026"]}, ) data = r.json()["data"]
{
"data": {
"object": "import_result",
"created": 1,
"updated": 1,
"skipped": 1
}
}
/contacts/{id}/whatsapp-call-permission
— Ask to call on WhatsApp
scope: write
Sends the contact a WhatsApp message with an Allow button. Once they tap it, whatsapp_call_permission on the contact is set and a call with channel whatsapp can be placed. Meta only allows it within 24 hours of the person's last WhatsApp message.
Parameters
| idpath | integer | Contact id (required) |
Responses
- 202OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 409No phone number or no WhatsApp number
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/contacts/1/whatsapp-call-permission \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/contacts/1/whatsapp-call-permission', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/contacts/1/whatsapp-call-permission'); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/contacts/1/whatsapp-call-permission", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "whatsapp_call_permission_request",
"contact_id": 1,
"message_id": 1,
"status": "string",
"note": "string"
}
}
/contacts/{id}
— Get contact
scope: read
Includes notes and custom fields collected during calls.
Parameters
| idpath | integer | Contact id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/contacts/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/contacts/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/contacts/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/contacts/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "contact",
"name": "string",
"phone": "+14155550100",
"email": "string",
"company": "string",
"tags": [
"string"
],
"do_not_call": true,
"source": "call",
"calls_count": 1,
"last_call_at": "2026-09-13T14:02:11+00:00",
"last_appointment_at": "2026-09-13T14:02:11+00:00",
"external_ids": {},
"whatsapp_call_permission": {
"permanent": true,
"until": "2026-09-13T14:02:11+00:00"
},
"created_at": "2026-09-13T14:02:11+00:00",
"notes": "string",
"custom": {}
}
}
/contacts/{id}
— Update contact
scope: write
Parameters
| idpath | integer | Contact id (required) |
Body (ContactInput)
| name | string | |
| phone | string | E.164 phone number |
| string | ||
| company | string | |
| tags | array | |
| notes | string | |
| custom | object | |
| do_not_call | boolean |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/contacts/1 \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"string","phone":"+14155550132","email":"string","company":"string","tags":["string"],"notes":"string","custom":{},"do_not_call":true}'const res = await fetch('https://ringhum.com/api/v1/contacts/1', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "string", "phone": "+14155550132", "email": "string", "company": "string", "tags": [ "string" ], "notes": "string", "custom": {}, "do_not_call": true }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/contacts/1', array ( 'name' => 'string', 'phone' => '+14155550132', 'email' => 'string', 'company' => 'string', 'tags' => array ( 0 => 'string', ), 'notes' => 'string', 'custom' => (object) array( ), 'do_not_call' => true, )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/contacts/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"string","phone":"+14155550132","email":"string","company":"string","tags":["string"],"notes":"string","custom":{},"do_not_call":true}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "contact",
"name": "string",
"phone": "+14155550100",
"email": "string",
"company": "string",
"tags": [
"string"
],
"do_not_call": true,
"source": "call",
"calls_count": 1,
"last_call_at": "2026-09-13T14:02:11+00:00",
"last_appointment_at": "2026-09-13T14:02:11+00:00",
"external_ids": {},
"whatsapp_call_permission": {
"permanent": true,
"until": "2026-09-13T14:02:11+00:00"
},
"created_at": "2026-09-13T14:02:11+00:00",
"notes": "string",
"custom": {}
}
}
/contacts/{id}
— Delete contact
scope: write
Soft-deletes the contact; calls and appointments are kept.
Parameters
| idpath | integer | Contact id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/contacts/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/contacts/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/contacts/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/contacts/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "string",
"deleted": true
}
}
Appointments
Availability and bookings made by assistants or you.
/appointments/availability
— Get availability
scope: read
Open slots computed from opening hours, holidays, booking rules, staff working hours and existing bookings.
Parameters
| datequery | string | First day to look at (workspace timezone). Defaults to today. |
| daysquery | integer | |
| service_idquery | integer | |
| staff_member_idquery | integer | |
| duration_minutesquery | integer |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/appointments/availability \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/appointments/availability', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/appointments/availability'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/appointments/availability", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "availability",
"timezone": "string",
"duration_minutes": 1,
"days": {}
}
}
/appointments
— List appointments
scope: read
Ordered by start time. Filter with status, from, to, contact_id or upcoming=true.
Parameters
| statusquery | string: scheduled | confirmed | completed | cancelled | no_show | |
| fromquery | string | |
| toquery | string | |
| upcomingquery | boolean | |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/appointments \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/appointments', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/appointments'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/appointments", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"paid_at": "2026-09-13T14:02:11+00:00",
"payment_link_sent_at": "2026-09-13T14:02:11+00:00",
"object": "appointment",
"title": "string",
"status": "scheduled",
"starts_at": "2026-09-13T14:02:11+00:00",
"ends_at": "2026-09-13T14:02:11+00:00",
"timezone": "string",
"customer": {
"name": "string",
"phone": "string",
"email": "string"
},
"contact_id": 1,
"service": {
"id": 1,
"name": "string",
"duration_minutes": 1
},
"staff_member": {
"id": 1,
"name": "string"
},
"location": {
"id": 1,
"name": "string",
"address": "string"
},
"call_id": 1,
"notes": "string",
"source": "ai",
"confirmation_sent_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/appointments
— Book appointment
scope: write
Books a slot, links or creates the contact and sends the confirmation email (unless notify: false). Returns 409 when the time is taken; pass force: true to book anyway.
Body (AppointmentInput)
| starts_at* | string | Workspace timezone unless an offset is given. |
| service_id | integer | |
| staff_member_id | integer | |
| location_id | integer | |
| title | string | |
| customer_name* | string | |
| customer_phone | string | E.164 phone number |
| customer_email | string | |
| notes | string | |
| contact_id | integer | |
| duration_minutes | integer | |
| notify | boolean | |
| force | boolean | Skip the availability check. |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 409Slot not available
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/appointments \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"starts_at":"2026-09-21T10:00:00","service_id":1,"customer_name":"Jane Doe","customer_phone":"+14155550100"}'const res = await fetch('https://ringhum.com/api/v1/appointments', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "starts_at": "2026-09-21T10:00:00", "service_id": 1, "customer_name": "Jane Doe", "customer_phone": "+14155550100" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/appointments', array ( 'starts_at' => '2026-09-21T10:00:00', 'service_id' => 1, 'customer_name' => 'Jane Doe', 'customer_phone' => '+14155550100', )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/appointments", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"starts_at":"2026-09-21T10:00:00","service_id":1,"customer_name":"Jane Doe","customer_phone":"+14155550100"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"paid_at": "2026-09-13T14:02:11+00:00",
"payment_link_sent_at": "2026-09-13T14:02:11+00:00",
"object": "appointment",
"title": "string",
"status": "scheduled",
"starts_at": "2026-09-13T14:02:11+00:00",
"ends_at": "2026-09-13T14:02:11+00:00",
"timezone": "string",
"customer": {
"name": "string",
"phone": "string",
"email": "string"
},
"contact_id": 1,
"service": {
"id": 1,
"name": "string",
"duration_minutes": 1
},
"staff_member": {
"id": 1,
"name": "string"
},
"location": {
"id": 1,
"name": "string",
"address": "string"
},
"call_id": 1,
"notes": "string",
"source": "ai",
"confirmation_sent_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/appointments/{id}
— Get appointment
scope: read
Parameters
| idpath | integer | Appointment id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/appointments/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/appointments/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/appointments/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/appointments/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"paid_at": "2026-09-13T14:02:11+00:00",
"payment_link_sent_at": "2026-09-13T14:02:11+00:00",
"object": "appointment",
"title": "string",
"status": "scheduled",
"starts_at": "2026-09-13T14:02:11+00:00",
"ends_at": "2026-09-13T14:02:11+00:00",
"timezone": "string",
"customer": {
"name": "string",
"phone": "string",
"email": "string"
},
"contact_id": 1,
"service": {
"id": 1,
"name": "string",
"duration_minutes": 1
},
"staff_member": {
"id": 1,
"name": "string"
},
"location": {
"id": 1,
"name": "string",
"address": "string"
},
"call_id": 1,
"notes": "string",
"source": "ai",
"confirmation_sent_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/appointments/{id}
— Update appointment
scope: write
Send starts_at to reschedule (customer is notified), or status, notes, title.
Parameters
| idpath | integer | Appointment id (required) |
Body (AppointmentUpdate)
| paid | boolean | Mark the deposit or prepayment as received (or not). |
| starts_at | string | |
| status | string | scheduled | confirmed | completed | no_show |
| notes | string | |
| title | string | |
| notify | boolean |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 409Slot not available
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/appointments/1 \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"paid":true,"starts_at":"2026-09-13T14:02:11+00:00","status":"scheduled","notes":"string","title":"string","notify":true}'const res = await fetch('https://ringhum.com/api/v1/appointments/1', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "paid": true, "starts_at": "2026-09-13T14:02:11+00:00", "status": "scheduled", "notes": "string", "title": "string", "notify": true }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/appointments/1', array ( 'paid' => true, 'starts_at' => '2026-09-13T14:02:11+00:00', 'status' => 'scheduled', 'notes' => 'string', 'title' => 'string', 'notify' => true, )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/appointments/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"paid":true,"starts_at":"2026-09-13T14:02:11+00:00","status":"scheduled","notes":"string","title":"string","notify":true}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"paid_at": "2026-09-13T14:02:11+00:00",
"payment_link_sent_at": "2026-09-13T14:02:11+00:00",
"object": "appointment",
"title": "string",
"status": "scheduled",
"starts_at": "2026-09-13T14:02:11+00:00",
"ends_at": "2026-09-13T14:02:11+00:00",
"timezone": "string",
"customer": {
"name": "string",
"phone": "string",
"email": "string"
},
"contact_id": 1,
"service": {
"id": 1,
"name": "string",
"duration_minutes": 1
},
"staff_member": {
"id": 1,
"name": "string"
},
"location": {
"id": 1,
"name": "string",
"address": "string"
},
"call_id": 1,
"notes": "string",
"source": "ai",
"confirmation_sent_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/appointments/{id}
— Cancel appointment
scope: write
Marks it cancelled and notifies the customer. Optional reason and notify query parameters.
Parameters
| idpath | integer | Appointment id (required) |
| reasonquery | string | |
| notifyquery | boolean |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/appointments/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/appointments/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/appointments/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/appointments/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"paid_at": "2026-09-13T14:02:11+00:00",
"payment_link_sent_at": "2026-09-13T14:02:11+00:00",
"object": "appointment",
"title": "string",
"status": "scheduled",
"starts_at": "2026-09-13T14:02:11+00:00",
"ends_at": "2026-09-13T14:02:11+00:00",
"timezone": "string",
"customer": {
"name": "string",
"phone": "string",
"email": "string"
},
"contact_id": 1,
"service": {
"id": 1,
"name": "string",
"duration_minutes": 1
},
"staff_member": {
"id": 1,
"name": "string"
},
"location": {
"id": 1,
"name": "string",
"address": "string"
},
"call_id": 1,
"notes": "string",
"source": "ai",
"confirmation_sent_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
Orders
Orders taken by assistants, by hand or through the API. Prices always come from your catalogue.
/orders
— List orders
scope: read
Newest first. Filter with status (comma-separated), fulfillment, from, to (created time), contact_id, open=true (not completed or cancelled) or q (order number, customer name or phone).
Parameters
| statusquery | string | One or more of new, confirmed, preparing, ready, out_for_delivery, completed, cancelled, comma-separated. |
| fulfillmentquery | string: pickup | delivery | dine_in | |
| fromquery | string | |
| toquery | string | |
| contact_idquery | integer | |
| openquery | boolean | |
| qquery | string | |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 403Orders are switched off for this workspace
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/orders \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/orders', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/orders'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/orders", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"paid_at": "2026-09-13T14:02:11+00:00",
"object": "order",
"number": 1042,
"reference": "#1042",
"status": "new",
"fulfillment": "pickup",
"customer": {
"name": "string",
"phone": "string",
"email": "string"
},
"address": "string",
"requested_for": "2026-09-13T14:02:11+00:00",
"notes": "string",
"items": [
{
"product_id": 1,
"sku": "string",
"name": "string",
"quantity": 1,
"unit_price": 1,
"unit_price_cents": 1,
"options": [
{
"group": null,
"name": null,
"price": null,
"price_cents": null
}
],
"notes": "string",
"total": 1,
"total_cents": 1
}
],
"currency": "EUR",
"subtotal": 1,
"subtotal_cents": 1,
"fee": 1,
"fee_cents": 1,
"total": 1,
"total_cents": 1,
"source": "ai",
"contact_id": 1,
"call_id": 1,
"assistant_id": 1,
"external_ids": {},
"confirmed_at": "2026-09-13T14:02:11+00:00",
"completed_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/orders
— Create order
scope: write
Prices the items from your catalogue, applies the delivery fee, the minimum order for delivery and opening hours, links or creates the contact and notifies the customer and your team (unless notify: false). Send skip_rules: true to accept an order outside opening hours or below the minimum. Fires order.created.
Body (OrderInput)
| items* | array | array of OrderItemInput |
| fulfillment | string | Defaults to the first mode the workspace offers. pickup | delivery | dine_in |
| customer_name* | string | |
| customer_phone | string | E.164 phone number |
| customer_email | string | |
| address | string | Required for delivery. |
| requested_for | string | Omit for as soon as possible. Workspace time zone when no offset is given. |
| notes | string | |
| contact_id | integer | |
| notify | boolean | |
| skip_rules | boolean | Accept the order outside opening hours, below the minimum or for a fulfillment mode that is switched off. |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422Items or order rules did not check out
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/orders \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"items":[{"product":"Margherita","quantity":2,"options":["Large"]},{"product_id":12}],"fulfillment":"delivery","customer_name":"Jordan Blake","customer_phone":"+14155550142","address":"12 Harbour Street","notes":"Ring twice"}'const res = await fetch('https://ringhum.com/api/v1/orders', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "items": [ { "product": "Margherita", "quantity": 2, "options": [ "Large" ] }, { "product_id": 12 } ], "fulfillment": "delivery", "customer_name": "Jordan Blake", "customer_phone": "+14155550142", "address": "12 Harbour Street", "notes": "Ring twice" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/orders', array ( 'items' => array ( 0 => array ( 'product' => 'Margherita', 'quantity' => 2, 'options' => array ( 0 => 'Large', ), ), 1 => array ( 'product_id' => 12, ), ), 'fulfillment' => 'delivery', 'customer_name' => 'Jordan Blake', 'customer_phone' => '+14155550142', 'address' => '12 Harbour Street', 'notes' => 'Ring twice', )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/orders", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"items":[{"product":"Margherita","quantity":2,"options":["Large"]},{"product_id":12}],"fulfillment":"delivery","customer_name":"Jordan Blake","customer_phone":"+14155550142","address":"12 Harbour Street","notes":"Ring twice"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"paid_at": "2026-09-13T14:02:11+00:00",
"object": "order",
"number": 1042,
"reference": "#1042",
"status": "new",
"fulfillment": "pickup",
"customer": {
"name": "string",
"phone": "string",
"email": "string"
},
"address": "string",
"requested_for": "2026-09-13T14:02:11+00:00",
"notes": "string",
"items": [
{
"product_id": 1,
"sku": "string",
"name": "string",
"quantity": 1,
"unit_price": 1,
"unit_price_cents": 1,
"options": [
{
"group": null,
"name": null,
"price": null,
"price_cents": null
}
],
"notes": "string",
"total": 1,
"total_cents": 1
}
],
"currency": "EUR",
"subtotal": 1,
"subtotal_cents": 1,
"fee": 1,
"fee_cents": 1,
"total": 1,
"total_cents": 1,
"source": "ai",
"contact_id": 1,
"call_id": 1,
"assistant_id": 1,
"external_ids": {},
"confirmed_at": "2026-09-13T14:02:11+00:00",
"completed_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/orders/quote
— Quote order
scope: read
Prices a basket without placing it: lines, fee, total and any problems (unknown items, missing required options, minimum order, fulfillment not offered). Nothing is saved.
Body (OrderQuoteInput)
| items* | array | array of OrderItemInput |
| fulfillment | string | pickup | delivery | dine_in |
Responses
- 200OK
- 401Missing or invalid API key.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/orders/quote \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"items":[{"product":"Margherita","quantity":2,"options":["Large"]}],"fulfillment":"delivery"}'const res = await fetch('https://ringhum.com/api/v1/orders/quote', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "items": [ { "product": "Margherita", "quantity": 2, "options": [ "Large" ] } ], "fulfillment": "delivery" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/orders/quote', array ( 'items' => array ( 0 => array ( 'product' => 'Margherita', 'quantity' => 2, 'options' => array ( 0 => 'Large', ), ), ), 'fulfillment' => 'delivery', )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/orders/quote", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"items":[{"product":"Margherita","quantity":2,"options":["Large"]}],"fulfillment":"delivery"}, ) data = r.json()["data"]
{
"data": {
"object": "order_quote",
"ok": true,
"fulfillment": "string",
"currency": "string",
"lines": [
{
"product_id": 1,
"sku": "string",
"name": "string",
"quantity": 1,
"unit_price": 1,
"unit_price_cents": 1,
"options": [
{
"group": null,
"name": null,
"price": null,
"price_cents": null
}
],
"notes": "string",
"total": 1,
"total_cents": 1
}
],
"subtotal": 1,
"subtotal_cents": 1,
"fee": 1,
"fee_cents": 1,
"total": 1,
"total_cents": 1,
"problems": [
"string"
]
}
}
/orders/{id}
— Get order
scope: read
Parameters
| idpath | integer | Order id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/orders/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/orders/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/orders/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/orders/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"paid_at": "2026-09-13T14:02:11+00:00",
"object": "order",
"number": 1042,
"reference": "#1042",
"status": "new",
"fulfillment": "pickup",
"customer": {
"name": "string",
"phone": "string",
"email": "string"
},
"address": "string",
"requested_for": "2026-09-13T14:02:11+00:00",
"notes": "string",
"items": [
{
"product_id": 1,
"sku": "string",
"name": "string",
"quantity": 1,
"unit_price": 1,
"unit_price_cents": 1,
"options": [
{
"group": null,
"name": null,
"price": null,
"price_cents": null
}
],
"notes": "string",
"total": 1,
"total_cents": 1
}
],
"currency": "EUR",
"subtotal": 1,
"subtotal_cents": 1,
"fee": 1,
"fee_cents": 1,
"total": 1,
"total_cents": 1,
"source": "ai",
"contact_id": 1,
"call_id": 1,
"assistant_id": 1,
"external_ids": {},
"confirmed_at": "2026-09-13T14:02:11+00:00",
"completed_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/orders/{id}
— Update order
scope: write
Move the order along (status), or change notes and requested_for. The customer hears about confirmed, ready and out-for-delivery (unless notify: false). Fires order.updated. Use DELETE to cancel.
Parameters
| idpath | integer | Order id (required) |
Body (OrderUpdate)
| paid | boolean | Mark the order as paid (or not). |
| status | string | new | confirmed | preparing | ready | out_for_delivery | completed |
| notes | string|null | |
| requested_for | string|null | |
| notify | boolean |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 409The order is cancelled
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/orders/1 \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"paid":true,"status":"new","notes":"string","requested_for":"2026-09-13T14:02:11+00:00","notify":true}'const res = await fetch('https://ringhum.com/api/v1/orders/1', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "paid": true, "status": "new", "notes": "string", "requested_for": "2026-09-13T14:02:11+00:00", "notify": true }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/orders/1', array ( 'paid' => true, 'status' => 'new', 'notes' => 'string', 'requested_for' => '2026-09-13T14:02:11+00:00', 'notify' => true, )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/orders/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"paid":true,"status":"new","notes":"string","requested_for":"2026-09-13T14:02:11+00:00","notify":true}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"paid_at": "2026-09-13T14:02:11+00:00",
"object": "order",
"number": 1042,
"reference": "#1042",
"status": "new",
"fulfillment": "pickup",
"customer": {
"name": "string",
"phone": "string",
"email": "string"
},
"address": "string",
"requested_for": "2026-09-13T14:02:11+00:00",
"notes": "string",
"items": [
{
"product_id": 1,
"sku": "string",
"name": "string",
"quantity": 1,
"unit_price": 1,
"unit_price_cents": 1,
"options": [
{
"group": null,
"name": null,
"price": null,
"price_cents": null
}
],
"notes": "string",
"total": 1,
"total_cents": 1
}
],
"currency": "EUR",
"subtotal": 1,
"subtotal_cents": 1,
"fee": 1,
"fee_cents": 1,
"total": 1,
"total_cents": 1,
"source": "ai",
"contact_id": 1,
"call_id": 1,
"assistant_id": 1,
"external_ids": {},
"confirmed_at": "2026-09-13T14:02:11+00:00",
"completed_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/orders/{id}
— Cancel order
scope: write
Marks it cancelled and notifies the customer and team. Optional reason and notify query parameters. Fires order.cancelled.
Parameters
| idpath | integer | Order id (required) |
| reasonquery | string | |
| notifyquery | boolean |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/orders/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/orders/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/orders/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/orders/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"paid_at": "2026-09-13T14:02:11+00:00",
"object": "order",
"number": 1042,
"reference": "#1042",
"status": "new",
"fulfillment": "pickup",
"customer": {
"name": "string",
"phone": "string",
"email": "string"
},
"address": "string",
"requested_for": "2026-09-13T14:02:11+00:00",
"notes": "string",
"items": [
{
"product_id": 1,
"sku": "string",
"name": "string",
"quantity": 1,
"unit_price": 1,
"unit_price_cents": 1,
"options": [
{
"group": null,
"name": null,
"price": null,
"price_cents": null
}
],
"notes": "string",
"total": 1,
"total_cents": 1
}
],
"currency": "EUR",
"subtotal": 1,
"subtotal_cents": 1,
"fee": 1,
"fee_cents": 1,
"total": 1,
"total_cents": 1,
"source": "ai",
"contact_id": 1,
"call_id": 1,
"assistant_id": 1,
"external_ids": {},
"confirmed_at": "2026-09-13T14:02:11+00:00",
"completed_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
Products
The menu or catalogue orders are priced from: items, categories, options and prices.
/products
— List products
scope: read
Your menu or catalogue, ordered by category. Filter with category, available or q.
Parameters
| categoryquery | string | |
| availablequery | boolean | |
| qquery | string | |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/products \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/products', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/products'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/products", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "product",
"name": "Margherita",
"description": "string",
"category": "Pizza",
"price": 9.5,
"price_cents": 950,
"currency": "EUR",
"options": [
{
"name": "Size",
"required": true,
"multiple": true,
"choices": [
{
"name": null,
"price": null,
"price_cents": null
}
]
}
],
"available": true,
"sort_order": 1,
"sku": "string",
"external_ids": {},
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/products
— Create product
scope: write
Send the price as price (decimal) or price_cents. Options are groups of choices, each with its own surcharge.
Body (ProductInput)
| name* | string | |
| description | string | |
| category | string | |
| price | number | Decimal price; or send price_cents. |
| price_cents | integer | |
| options | array | array of ProductOption |
| available | boolean | |
| sort_order | integer | |
| sku | string | |
| external_ids | object |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/products \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"Margherita","category":"Pizza","price":9.5,"options":[{"name":"Size","required":true,"choices":[{"name":"Medium","price":0},{"name":"Large","price":2.5}]},{"name":"Extras","multiple":true,"choices":[{"name":"Extra cheese","price":1.5}]}]}'const res = await fetch('https://ringhum.com/api/v1/products', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "Margherita", "category": "Pizza", "price": 9.5, "options": [ { "name": "Size", "required": true, "choices": [ { "name": "Medium", "price": 0 }, { "name": "Large", "price": 2.5 } ] }, { "name": "Extras", "multiple": true, "choices": [ { "name": "Extra cheese", "price": 1.5 } ] } ] }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/products', array ( 'name' => 'Margherita', 'category' => 'Pizza', 'price' => 9.5, 'options' => array ( 0 => array ( 'name' => 'Size', 'required' => true, 'choices' => array ( 0 => array ( 'name' => 'Medium', 'price' => 0, ), 1 => array ( 'name' => 'Large', 'price' => 2.5, ), ), ), 1 => array ( 'name' => 'Extras', 'multiple' => true, 'choices' => array ( 0 => array ( 'name' => 'Extra cheese', 'price' => 1.5, ), ), ), ), )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/products", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"Margherita","category":"Pizza","price":9.5,"options":[{"name":"Size","required":true,"choices":[{"name":"Medium","price":0},{"name":"Large","price":2.5}]},{"name":"Extras","multiple":true,"choices":[{"name":"Extra cheese","price":1.5}]}]}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "product",
"name": "Margherita",
"description": "string",
"category": "Pizza",
"price": 9.5,
"price_cents": 950,
"currency": "EUR",
"options": [
{
"name": "Size",
"required": true,
"multiple": true,
"choices": [
{
"name": null,
"price": null,
"price_cents": null
}
]
}
],
"available": true,
"sort_order": 1,
"sku": "string",
"external_ids": {},
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/products/{id}
— Get product
scope: read
Parameters
| idpath | integer | Product id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/products/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/products/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/products/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/products/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "product",
"name": "Margherita",
"description": "string",
"category": "Pizza",
"price": 9.5,
"price_cents": 950,
"currency": "EUR",
"options": [
{
"name": "Size",
"required": true,
"multiple": true,
"choices": [
{
"name": null,
"price": null,
"price_cents": null
}
]
}
],
"available": true,
"sort_order": 1,
"sku": "string",
"external_ids": {},
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/products/{id}
— Update product
scope: write
Partial update. Set available: false to take an item off the menu for now; sending options replaces all option groups.
Parameters
| idpath | integer | Product id (required) |
Body (ProductInput)
| name* | string | |
| description | string | |
| category | string | |
| price | number | Decimal price; or send price_cents. |
| price_cents | integer | |
| options | array | array of ProductOption |
| available | boolean | |
| sort_order | integer | |
| sku | string | |
| external_ids | object |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/products/1 \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"Margherita","category":"Pizza","price":9.5,"options":[{"name":"Size","required":true,"choices":[{"name":"Medium","price":0},{"name":"Large","price":2.5}]},{"name":"Extras","multiple":true,"choices":[{"name":"Extra cheese","price":1.5}]}]}'const res = await fetch('https://ringhum.com/api/v1/products/1', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "Margherita", "category": "Pizza", "price": 9.5, "options": [ { "name": "Size", "required": true, "choices": [ { "name": "Medium", "price": 0 }, { "name": "Large", "price": 2.5 } ] }, { "name": "Extras", "multiple": true, "choices": [ { "name": "Extra cheese", "price": 1.5 } ] } ] }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/products/1', array ( 'name' => 'Margherita', 'category' => 'Pizza', 'price' => 9.5, 'options' => array ( 0 => array ( 'name' => 'Size', 'required' => true, 'choices' => array ( 0 => array ( 'name' => 'Medium', 'price' => 0, ), 1 => array ( 'name' => 'Large', 'price' => 2.5, ), ), ), 1 => array ( 'name' => 'Extras', 'multiple' => true, 'choices' => array ( 0 => array ( 'name' => 'Extra cheese', 'price' => 1.5, ), ), ), ), )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/products/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"Margherita","category":"Pizza","price":9.5,"options":[{"name":"Size","required":true,"choices":[{"name":"Medium","price":0},{"name":"Large","price":2.5}]},{"name":"Extras","multiple":true,"choices":[{"name":"Extra cheese","price":1.5}]}]}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "product",
"name": "Margherita",
"description": "string",
"category": "Pizza",
"price": 9.5,
"price_cents": 950,
"currency": "EUR",
"options": [
{
"name": "Size",
"required": true,
"multiple": true,
"choices": [
{
"name": null,
"price": null,
"price_cents": null
}
]
}
],
"available": true,
"sort_order": 1,
"sku": "string",
"external_ids": {},
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/products/{id}
— Delete product
scope: write
Removes it from the catalogue. Orders already placed keep their copy of the item.
Parameters
| idpath | integer | Product id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/products/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/products/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/products/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/products/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "string",
"deleted": true
}
}
Reservations
Room reservations taken by assistants, by hand, through the API or imported from Booking.com, Airbnb and VRBO calendars.
/reservations/availability
— Get availability
scope: read
Free units per room type for a stay, with the price for those nights (seasonal rates applied). Counts every active reservation, including blocks imported from booking channels.
Parameters
| check_inquery | string | (required) |
| check_outquery | string | (required) |
| adultsquery | integer | |
| childrenquery | integer | |
| room_idquery | integer |
Responses
- 200OK
- 401Missing or invalid API key.
- 403Hotels are switched off for this workspace
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/reservations/availability \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/reservations/availability', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/reservations/availability'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/reservations/availability", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "availability",
"check_in": "string",
"check_out": "string",
"nights": 1,
"currency": "string",
"rooms": [
{
"room_id": 1,
"name": "string",
"category": "string",
"quantity": 1,
"free": 1,
"fits": true,
"capacity_adults": 1,
"capacity_children": 1,
"nightly": 1,
"nightly_cents": 1,
"total": 1,
"total_cents": 1
}
]
}
}
/reservations
— List reservations
scope: read
Latest check-in first. Filter with status (comma-separated), room_id, source, from, to (stays with a night in the window), contact_id, upcoming=true, active=true (holding a room) or q (reservation number, guest name, phone or email).
Parameters
| statusquery | string | One or more of requested, confirmed, checked_in, checked_out, cancelled, no_show, comma-separated. |
| room_idquery | integer | |
| sourcequery | string: ai | manual | api | ical | |
| fromquery | string | |
| toquery | string | |
| contact_idquery | integer | |
| upcomingquery | boolean | |
| activequery | boolean | |
| qquery | string | |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 403Hotels are switched off for this workspace
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/reservations \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/reservations', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/reservations'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/reservations", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "reservation",
"number": 1017,
"reference": "#1017",
"status": "requested",
"room": {
"id": 1,
"name": "string",
"category": "string"
},
"room_id": 1,
"check_in": "2026-10-03",
"check_out": "2026-10-06",
"nights": 1,
"adults": 1,
"children": 1,
"guest": {
"name": "string",
"phone": "string",
"email": "string"
},
"notes": "string",
"currency": "EUR",
"nightly": 1,
"nightly_cents": 1,
"total": 1,
"total_cents": 1,
"source": "ai",
"external_source": "booking.com",
"external_uid": "string",
"contact_id": 1,
"call_id": 1,
"assistant_id": 1,
"external_ids": {},
"confirmed_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/reservations
— Create reservation
scope: write
Prices the stay from the room type and its seasonal rates, checks capacity and availability (including imported channel blocks), links or creates the contact and notifies the guest and your team (unless notify: false). Send skip_rules: true to accept a stay shorter or longer than the workspace allows or starting in the past. Fires reservation.created.
Body (ReservationInput)
| room_id | integer | Room type id; or send `room` by name. |
| room | string | |
| check_in* | string | |
| check_out* | string | |
| adults | integer | |
| children | integer | |
| guest_name* | string | |
| guest_phone | string | E.164 phone number |
| guest_email | string | |
| notes | string | |
| contact_id | integer | |
| notify | boolean | |
| skip_rules | boolean | Accept a stay outside the minimum / maximum nights or starting in the past. |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422Dates, capacity or availability did not check out
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/reservations \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"room":"Sea-view double","check_in":"2026-10-03","check_out":"2026-10-06","adults":2,"children":1,"guest_name":"Jordan Blake","guest_phone":"+14155550142","guest_email":"jordan@example.com","notes":"Late arrival"}'const res = await fetch('https://ringhum.com/api/v1/reservations', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "room": "Sea-view double", "check_in": "2026-10-03", "check_out": "2026-10-06", "adults": 2, "children": 1, "guest_name": "Jordan Blake", "guest_phone": "+14155550142", "guest_email": "jordan@example.com", "notes": "Late arrival" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/reservations', array ( 'room' => 'Sea-view double', 'check_in' => '2026-10-03', 'check_out' => '2026-10-06', 'adults' => 2, 'children' => 1, 'guest_name' => 'Jordan Blake', 'guest_phone' => '+14155550142', 'guest_email' => 'jordan@example.com', 'notes' => 'Late arrival', )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/reservations", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"room":"Sea-view double","check_in":"2026-10-03","check_out":"2026-10-06","adults":2,"children":1,"guest_name":"Jordan Blake","guest_phone":"+14155550142","guest_email":"jordan@example.com","notes":"Late arrival"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "reservation",
"number": 1017,
"reference": "#1017",
"status": "requested",
"room": {
"id": 1,
"name": "string",
"category": "string"
},
"room_id": 1,
"check_in": "2026-10-03",
"check_out": "2026-10-06",
"nights": 1,
"adults": 1,
"children": 1,
"guest": {
"name": "string",
"phone": "string",
"email": "string"
},
"notes": "string",
"currency": "EUR",
"nightly": 1,
"nightly_cents": 1,
"total": 1,
"total_cents": 1,
"source": "ai",
"external_source": "booking.com",
"external_uid": "string",
"contact_id": 1,
"call_id": 1,
"assistant_id": 1,
"external_ids": {},
"confirmed_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/reservations/{id}
— Get reservation
scope: read
Parameters
| idpath | integer | Reservation id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/reservations/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/reservations/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/reservations/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/reservations/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "reservation",
"number": 1017,
"reference": "#1017",
"status": "requested",
"room": {
"id": 1,
"name": "string",
"category": "string"
},
"room_id": 1,
"check_in": "2026-10-03",
"check_out": "2026-10-06",
"nights": 1,
"adults": 1,
"children": 1,
"guest": {
"name": "string",
"phone": "string",
"email": "string"
},
"notes": "string",
"currency": "EUR",
"nightly": 1,
"nightly_cents": 1,
"total": 1,
"total_cents": 1,
"source": "ai",
"external_source": "booking.com",
"external_uid": "string",
"contact_id": 1,
"call_id": 1,
"assistant_id": 1,
"external_ids": {},
"confirmed_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/reservations/{id}
— Update reservation
scope: write
Move the stay along (status: confirmed, checked_in, checked_out, no_show), or change guest details, adults, children and notes. The guest hears about confirmation (unless notify: false). Fires reservation.updated. Use DELETE to cancel.
Parameters
| idpath | integer | Reservation id (required) |
Body (ReservationUpdate)
| status | string | requested | confirmed | checked_in | checked_out | no_show |
| guest_name | string | |
| guest_phone | string | E.164 phone number |
| guest_email | string|null | |
| adults | integer | |
| children | integer | |
| notes | string|null | |
| notify | boolean |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 409The reservation is cancelled
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/reservations/1 \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"status":"requested","guest_name":"string","guest_phone":"+14155550132","guest_email":"string","adults":1,"children":1,"notes":"string","notify":true}'const res = await fetch('https://ringhum.com/api/v1/reservations/1', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "status": "requested", "guest_name": "string", "guest_phone": "+14155550132", "guest_email": "string", "adults": 1, "children": 1, "notes": "string", "notify": true }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/reservations/1', array ( 'status' => 'requested', 'guest_name' => 'string', 'guest_phone' => '+14155550132', 'guest_email' => 'string', 'adults' => 1, 'children' => 1, 'notes' => 'string', 'notify' => true, )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/reservations/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"status":"requested","guest_name":"string","guest_phone":"+14155550132","guest_email":"string","adults":1,"children":1,"notes":"string","notify":true}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "reservation",
"number": 1017,
"reference": "#1017",
"status": "requested",
"room": {
"id": 1,
"name": "string",
"category": "string"
},
"room_id": 1,
"check_in": "2026-10-03",
"check_out": "2026-10-06",
"nights": 1,
"adults": 1,
"children": 1,
"guest": {
"name": "string",
"phone": "string",
"email": "string"
},
"notes": "string",
"currency": "EUR",
"nightly": 1,
"nightly_cents": 1,
"total": 1,
"total_cents": 1,
"source": "ai",
"external_source": "booking.com",
"external_uid": "string",
"contact_id": 1,
"call_id": 1,
"assistant_id": 1,
"external_ids": {},
"confirmed_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/reservations/{id}
— Cancel reservation
scope: write
Marks it cancelled and notifies the guest and team. Optional reason and notify query parameters. Fires reservation.cancelled.
Parameters
| idpath | integer | Reservation id (required) |
| reasonquery | string | |
| notifyquery | boolean |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/reservations/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/reservations/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/reservations/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/reservations/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "reservation",
"number": 1017,
"reference": "#1017",
"status": "requested",
"room": {
"id": 1,
"name": "string",
"category": "string"
},
"room_id": 1,
"check_in": "2026-10-03",
"check_out": "2026-10-06",
"nights": 1,
"adults": 1,
"children": 1,
"guest": {
"name": "string",
"phone": "string",
"email": "string"
},
"notes": "string",
"currency": "EUR",
"nightly": 1,
"nightly_cents": 1,
"total": 1,
"total_cents": 1,
"source": "ai",
"external_source": "booking.com",
"external_uid": "string",
"contact_id": 1,
"call_id": 1,
"assistant_id": 1,
"external_ids": {},
"confirmed_at": "2026-09-13T14:02:11+00:00",
"cancelled_at": "2026-09-13T14:02:11+00:00",
"cancel_reason": "string",
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
Rooms
The room types reservations are booked from: capacity, units, nightly and seasonal prices, calendar sync.
/rooms
— List rooms
scope: read
Your room types in display order. Filter with category, available or q.
Parameters
| categoryquery | string | |
| availablequery | boolean | |
| qquery | string | |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/rooms \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/rooms', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/rooms'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/rooms", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "room",
"name": "Sea-view double",
"description": "string",
"category": "Double room",
"capacity_adults": 1,
"capacity_children": 1,
"quantity": 1,
"price": 95,
"price_cents": 9500,
"currency": "EUR",
"seasonal_rates": [
{
"from": "string",
"to": "string",
"price": 120,
"price_cents": 12000
}
],
"amenities": [
"string"
],
"ical_import_urls": [
{
"label": "string",
"url": "string"
}
],
"ical_export_url": "string",
"ical_synced_at": "2026-09-13T14:02:11+00:00",
"ical_error": "string",
"available": true,
"sort_order": 1,
"external_ids": {},
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/rooms
— Create room
scope: write
Send the nightly price as price (decimal) or price_cents. quantity is how many identical units you have. Seasonal rates override the price for date ranges; ical_import_urls are calendars to import from booking channels.
Body (RoomInput)
| name* | string | |
| description | string | |
| category | string | |
| capacity_adults | integer | |
| capacity_children | integer | |
| quantity | integer | |
| price | number | Decimal nightly price; or send price_cents. |
| price_cents | integer | |
| seasonal_rates | array | array of SeasonalRate |
| amenities | array | |
| ical_import_urls | array | |
| available | boolean | |
| sort_order | integer | |
| external_ids | object |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/rooms \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"Sea-view double","category":"Double room","capacity_adults":2,"capacity_children":1,"quantity":4,"price":95,"seasonal_rates":[{"from":"2026-07-01","to":"2026-08-31","price":140}],"amenities":["Wi-Fi","Balcony","Sea view"]}'const res = await fetch('https://ringhum.com/api/v1/rooms', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "Sea-view double", "category": "Double room", "capacity_adults": 2, "capacity_children": 1, "quantity": 4, "price": 95, "seasonal_rates": [ { "from": "2026-07-01", "to": "2026-08-31", "price": 140 } ], "amenities": [ "Wi-Fi", "Balcony", "Sea view" ] }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/rooms', array ( 'name' => 'Sea-view double', 'category' => 'Double room', 'capacity_adults' => 2, 'capacity_children' => 1, 'quantity' => 4, 'price' => 95, 'seasonal_rates' => array ( 0 => array ( 'from' => '2026-07-01', 'to' => '2026-08-31', 'price' => 140, ), ), 'amenities' => array ( 0 => 'Wi-Fi', 1 => 'Balcony', 2 => 'Sea view', ), )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/rooms", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"Sea-view double","category":"Double room","capacity_adults":2,"capacity_children":1,"quantity":4,"price":95,"seasonal_rates":[{"from":"2026-07-01","to":"2026-08-31","price":140}],"amenities":["Wi-Fi","Balcony","Sea view"]}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "room",
"name": "Sea-view double",
"description": "string",
"category": "Double room",
"capacity_adults": 1,
"capacity_children": 1,
"quantity": 1,
"price": 95,
"price_cents": 9500,
"currency": "EUR",
"seasonal_rates": [
{
"from": "string",
"to": "string",
"price": 120,
"price_cents": 12000
}
],
"amenities": [
"string"
],
"ical_import_urls": [
{
"label": "string",
"url": "string"
}
],
"ical_export_url": "string",
"ical_synced_at": "2026-09-13T14:02:11+00:00",
"ical_error": "string",
"available": true,
"sort_order": 1,
"external_ids": {},
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/rooms/{id}
— Get room
scope: read
Parameters
| idpath | integer | Room id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/rooms/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/rooms/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/rooms/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/rooms/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "room",
"name": "Sea-view double",
"description": "string",
"category": "Double room",
"capacity_adults": 1,
"capacity_children": 1,
"quantity": 1,
"price": 95,
"price_cents": 9500,
"currency": "EUR",
"seasonal_rates": [
{
"from": "string",
"to": "string",
"price": 120,
"price_cents": 12000
}
],
"amenities": [
"string"
],
"ical_import_urls": [
{
"label": "string",
"url": "string"
}
],
"ical_export_url": "string",
"ical_synced_at": "2026-09-13T14:02:11+00:00",
"ical_error": "string",
"available": true,
"sort_order": 1,
"external_ids": {},
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/rooms/{id}
— Update room
scope: write
Partial update. Set available: false to stop offering the room type; sending seasonal_rates, amenities or ical_import_urls replaces the whole list.
Parameters
| idpath | integer | Room id (required) |
Body (RoomInput)
| name* | string | |
| description | string | |
| category | string | |
| capacity_adults | integer | |
| capacity_children | integer | |
| quantity | integer | |
| price | number | Decimal nightly price; or send price_cents. |
| price_cents | integer | |
| seasonal_rates | array | array of SeasonalRate |
| amenities | array | |
| ical_import_urls | array | |
| available | boolean | |
| sort_order | integer | |
| external_ids | object |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/rooms/1 \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"Sea-view double","category":"Double room","capacity_adults":2,"capacity_children":1,"quantity":4,"price":95,"seasonal_rates":[{"from":"2026-07-01","to":"2026-08-31","price":140}],"amenities":["Wi-Fi","Balcony","Sea view"]}'const res = await fetch('https://ringhum.com/api/v1/rooms/1', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "Sea-view double", "category": "Double room", "capacity_adults": 2, "capacity_children": 1, "quantity": 4, "price": 95, "seasonal_rates": [ { "from": "2026-07-01", "to": "2026-08-31", "price": 140 } ], "amenities": [ "Wi-Fi", "Balcony", "Sea view" ] }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/rooms/1', array ( 'name' => 'Sea-view double', 'category' => 'Double room', 'capacity_adults' => 2, 'capacity_children' => 1, 'quantity' => 4, 'price' => 95, 'seasonal_rates' => array ( 0 => array ( 'from' => '2026-07-01', 'to' => '2026-08-31', 'price' => 140, ), ), 'amenities' => array ( 0 => 'Wi-Fi', 1 => 'Balcony', 2 => 'Sea view', ), )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/rooms/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"Sea-view double","category":"Double room","capacity_adults":2,"capacity_children":1,"quantity":4,"price":95,"seasonal_rates":[{"from":"2026-07-01","to":"2026-08-31","price":140}],"amenities":["Wi-Fi","Balcony","Sea view"]}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "room",
"name": "Sea-view double",
"description": "string",
"category": "Double room",
"capacity_adults": 1,
"capacity_children": 1,
"quantity": 1,
"price": 95,
"price_cents": 9500,
"currency": "EUR",
"seasonal_rates": [
{
"from": "string",
"to": "string",
"price": 120,
"price_cents": 12000
}
],
"amenities": [
"string"
],
"ical_import_urls": [
{
"label": "string",
"url": "string"
}
],
"ical_export_url": "string",
"ical_synced_at": "2026-09-13T14:02:11+00:00",
"ical_error": "string",
"available": true,
"sort_order": 1,
"external_ids": {},
"created_at": "2026-09-13T14:02:11+00:00",
"updated_at": "2026-09-13T14:02:11+00:00"
}
}
/rooms/{id}
— Delete room
scope: write
Removes the room type. Reservations already made keep their dates and lose the room.
Parameters
| idpath | integer | Room id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/rooms/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/rooms/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/rooms/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/rooms/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "string",
"deleted": true
}
}
Inbox
Messages, callbacks and leads that need a human.
/inbox
— List inbox items
scope: read
Messages, callback requests, leads, voicemails and inbound texts. status=open returns everything not done.
Parameters
| statusquery | string: open | in_progress | done | |
| typequery | string: message | callback | lead | voicemail | sms | |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/inbox \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/inbox', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/inbox'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/inbox", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "inbox_item",
"type": "message",
"title": "string",
"body": "string",
"status": "open",
"priority": "normal",
"contact_id": 1,
"call_id": 1,
"assigned_to": 1,
"due_at": "2026-09-13T14:02:11+00:00",
"done_at": "2026-09-13T14:02:11+00:00",
"meta": {},
"created_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/inbox/{id}
— Get inbox item
scope: read
Parameters
| idpath | integer | Inbox item id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/inbox/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/inbox/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/inbox/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/inbox/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "inbox_item",
"type": "message",
"title": "string",
"body": "string",
"status": "open",
"priority": "normal",
"contact_id": 1,
"call_id": 1,
"assigned_to": 1,
"due_at": "2026-09-13T14:02:11+00:00",
"done_at": "2026-09-13T14:02:11+00:00",
"meta": {},
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/inbox/{id}
— Update inbox item
scope: write
Change status, priority or assigned_to (a member id).
Parameters
| idpath | integer | Inbox item id (required) |
Body (InboxUpdate)
| status | string | open | in_progress | done |
| priority | string | normal | high |
| assigned_to | integer|null |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/inbox/1 \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"status":"open","priority":"normal","assigned_to":1}'const res = await fetch('https://ringhum.com/api/v1/inbox/1', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "status": "open", "priority": "normal", "assigned_to": 1 }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/inbox/1', array ( 'status' => 'open', 'priority' => 'normal', 'assigned_to' => 1, )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/inbox/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"status":"open","priority":"normal","assigned_to":1}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "inbox_item",
"type": "message",
"title": "string",
"body": "string",
"status": "open",
"priority": "normal",
"contact_id": 1,
"call_id": 1,
"assigned_to": 1,
"due_at": "2026-09-13T14:02:11+00:00",
"done_at": "2026-09-13T14:02:11+00:00",
"meta": {},
"created_at": "2026-09-13T14:02:11+00:00"
}
}
Messages
Text and WhatsApp messages sent and received by the workspace, and sending your own.
/messages
— List messages
scope: read
Texts and WhatsApp messages, newest first: the ones people sent to your numbers, the assistant's replies, confirmations and reminders, and messages sent by hand or through this API. Filter by channel, direction, contact_id, a date range on created_at and q (body or phone number).
Parameters
| channelquery | string: sms | whatsapp | instagram | messenger | web | web = the website chat widget. |
| directionquery | string: inbound | outbound | |
| contact_idquery | integer | |
| fromquery | string | Created at or after. |
| toquery | string | Created at or before. |
| qquery | string | Matches the body, or a phone number when it contains digits. |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 403Texting is off and no number is connected to WhatsApp
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/messages \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/messages', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/messages'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/messages", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "message",
"channel": "sms",
"direction": "inbound",
"status": "queued",
"from": "+14155550132",
"to": "+14155550132",
"body": "string",
"media": [
{
"url": "string",
"content_type": "string"
}
],
"sender_name": "string",
"contact_id": 1,
"call_id": 1,
"appointment_id": 1,
"purpose": "string",
"ai_generated": true,
"error": "string",
"cost_cents": 1,
"sent_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/messages
— Send message
scope: write
Sends a message from the workspace. channel auto (the default) picks WhatsApp when the person wrote to your WhatsApp number in the last 24 hours (Meta's window for free-form replies) and SMS otherwise; whatsapp or sms forces one. Returns 201 with the message; when the carrier turns it down afterwards the message comes back with status failed and the reason in error. Anything refused before sending (no WhatsApp number, no credit, a text to another country, a WhatsApp message outside the 24-hour window) is a 422. Texts go to numbers in the sending number's country only; WhatsApp messages go anywhere but only inside the 24-hour window, since template messages are not supported yet.
Body (TextMessageInput)
| to* | string | E.164 phone number, or the address of an Instagram, Messenger or website chat as it appears in a message's `from` (for example `web:12`, `ig:…`, `fb:…`) to reply there. |
| body* | string | |
| channel | string | Optional; `auto` by default (WhatsApp inside the 24-hour window, otherwise SMS). Chat addresses pick their own channel. auto | whatsapp | sms | instagram | messenger | web |
| contact_id | integer | Optional; links the message to a contact. |
Responses
- 201OK
- 401Missing or invalid API key.
- 403Texting is off and no number is connected to WhatsApp
- 422The body failed validation, or the message could not be sent
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/messages \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"to":"+15551234567","body":"Your appointment is confirmed for Tuesday at 10:00.","channel":"auto"}'const res = await fetch('https://ringhum.com/api/v1/messages', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "to": "+15551234567", "body": "Your appointment is confirmed for Tuesday at 10:00.", "channel": "auto" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/messages', array ( 'to' => '+15551234567', 'body' => 'Your appointment is confirmed for Tuesday at 10:00.', 'channel' => 'auto', )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/messages", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"to":"+15551234567","body":"Your appointment is confirmed for Tuesday at 10:00.","channel":"auto"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "message",
"channel": "sms",
"direction": "inbound",
"status": "queued",
"from": "+14155550132",
"to": "+14155550132",
"body": "string",
"media": [
{
"url": "string",
"content_type": "string"
}
],
"sender_name": "string",
"contact_id": 1,
"call_id": 1,
"appointment_id": 1,
"purpose": "string",
"ai_generated": true,
"error": "string",
"cost_cents": 1,
"sent_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/messages/{id}
— Get message
scope: read
Parameters
| idpath | integer | Message id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/messages/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/messages/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/messages/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/messages/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "message",
"channel": "sms",
"direction": "inbound",
"status": "queued",
"from": "+14155550132",
"to": "+14155550132",
"body": "string",
"media": [
{
"url": "string",
"content_type": "string"
}
],
"sender_name": "string",
"contact_id": 1,
"call_id": 1,
"appointment_id": 1,
"purpose": "string",
"ai_generated": true,
"error": "string",
"cost_cents": 1,
"sent_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
Campaigns
Outbound calling campaigns.
/campaigns
— List campaigns
scope: read
Parameters
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/campaigns \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/campaigns', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/campaigns'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/campaigns", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "campaign",
"name": "string",
"description": "string",
"status": "draft",
"assistant_id": 1,
"phone_number_id": 1,
"starts_at": "2026-09-13T14:02:11+00:00",
"window": {
"start": "09:00",
"end": "18:00",
"days": [
"string"
],
"timezone": "string"
},
"concurrency": 1,
"max_attempts": 1,
"retry_minutes": 1,
"instructions": "string",
"progress": {
"total": 1,
"pending": 1,
"completed": 1,
"no_answer": 1,
"failed": 1,
"skipped": 1
},
"created_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/campaigns
— Create campaign
scope: write
Creates a draft. Add contacts with contact_ids and/or tag, then start it with the transition endpoint.
Body (CampaignInput)
| name* | string | |
| assistant_id* | integer | |
| phone_number_id | integer | |
| description | string | |
| instructions | string | Extra prompt for these calls only. |
| starts_at | string | |
| window_start | string | |
| window_end | string | |
| days | array | |
| timezone | string | |
| concurrency | integer | |
| max_attempts | integer | |
| retry_minutes | integer | |
| contact_ids | array | |
| tag | string | Add every contact with this tag. |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/campaigns \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"Recall reminders","assistant_id":1,"tag":"recall-2026","window_start":"10:00","window_end":"16:00"}'const res = await fetch('https://ringhum.com/api/v1/campaigns', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "Recall reminders", "assistant_id": 1, "tag": "recall-2026", "window_start": "10:00", "window_end": "16:00" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/campaigns', array ( 'name' => 'Recall reminders', 'assistant_id' => 1, 'tag' => 'recall-2026', 'window_start' => '10:00', 'window_end' => '16:00', )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/campaigns", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"Recall reminders","assistant_id":1,"tag":"recall-2026","window_start":"10:00","window_end":"16:00"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "campaign",
"name": "string",
"description": "string",
"status": "draft",
"assistant_id": 1,
"phone_number_id": 1,
"starts_at": "2026-09-13T14:02:11+00:00",
"window": {
"start": "09:00",
"end": "18:00",
"days": [
"string"
],
"timezone": "string"
},
"concurrency": 1,
"max_attempts": 1,
"retry_minutes": 1,
"instructions": "string",
"progress": {
"total": 1,
"pending": 1,
"completed": 1,
"no_answer": 1,
"failed": 1,
"skipped": 1
},
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/campaigns/{id}
— Get campaign
scope: read
Includes live progress counts.
Parameters
| idpath | integer | Campaign id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/campaigns/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/campaigns/1', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/campaigns/1'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/campaigns/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "campaign",
"name": "string",
"description": "string",
"status": "draft",
"assistant_id": 1,
"phone_number_id": 1,
"starts_at": "2026-09-13T14:02:11+00:00",
"window": {
"start": "09:00",
"end": "18:00",
"days": [
"string"
],
"timezone": "string"
},
"concurrency": 1,
"max_attempts": 1,
"retry_minutes": 1,
"instructions": "string",
"progress": {
"total": 1,
"pending": 1,
"completed": 1,
"no_answer": 1,
"failed": 1,
"skipped": 1
},
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/campaigns/{id}/results
— List campaign results
scope: read
One row per contact with status, outcome, attempts and the last call id.
Parameters
| idpath | integer | Campaign id (required) |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/campaigns/1/results \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/campaigns/1/results', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/campaigns/1/results'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/campaigns/1/results", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"object": "campaign_contact",
"contact": {
"id": 1,
"object": "contact",
"name": "string",
"phone": "+14155550100",
"email": "string",
"company": "string",
"tags": [
"string"
],
"do_not_call": true,
"source": "call",
"calls_count": 1,
"last_call_at": "2026-09-13T14:02:11+00:00",
"last_appointment_at": "2026-09-13T14:02:11+00:00",
"external_ids": {},
"whatsapp_call_permission": {
"permanent": true,
"until": "2026-09-13T14:02:11+00:00"
},
"created_at": "2026-09-13T14:02:11+00:00"
},
"status": "pending",
"outcome": "string",
"attempts": 1,
"last_call_id": 1,
"last_attempt_at": "2026-09-13T14:02:11+00:00",
"next_attempt_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/campaigns/{id}/contacts
— Add contacts to campaign
scope: write
Contacts already in the campaign are ignored.
Parameters
| idpath | integer | Campaign id (required) |
Body (CampaignContactsInput)
| contact_ids* | array |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/campaigns/1/contacts \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"contact_ids":[12,14]}'const res = await fetch('https://ringhum.com/api/v1/campaigns/1/contacts', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "contact_ids": [ 12, 14 ] }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/campaigns/1/contacts', array ( 'contact_ids' => array ( 0 => 12, 1 => 14, ), )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/campaigns/1/contacts", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"contact_ids":[12,14]}, ) data = r.json()["data"]
{
"data": {
"object": "campaign_contacts",
"added": 1,
"campaign": {
"id": 1,
"object": "campaign",
"name": "string",
"description": "string",
"status": "draft",
"assistant_id": 1,
"phone_number_id": 1,
"starts_at": "2026-09-13T14:02:11+00:00",
"window": {
"start": "09:00",
"end": "18:00",
"days": [
"string"
],
"timezone": "string"
},
"concurrency": 1,
"max_attempts": 1,
"retry_minutes": 1,
"instructions": "string",
"progress": {
"total": 1,
"pending": 1,
"completed": 1,
"no_answer": 1,
"failed": 1,
"skipped": 1
},
"created_at": "2026-09-13T14:02:11+00:00"
}
}
}
/campaigns/{id}/transition
— Start, pause or cancel campaign
scope: write
Body: {"action": "start" | "pause" | "cancel"}. Calls are only placed inside the calling window.
Parameters
| idpath | integer | Campaign id (required) |
Body (CampaignTransition)
| action* | string | start | pause | cancel |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/campaigns/1/transition \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"action":"start"}'const res = await fetch('https://ringhum.com/api/v1/campaigns/1/transition', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "action": "start" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/campaigns/1/transition', array ( 'action' => 'start', )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/campaigns/1/transition", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"action":"start"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "campaign",
"name": "string",
"description": "string",
"status": "draft",
"assistant_id": 1,
"phone_number_id": 1,
"starts_at": "2026-09-13T14:02:11+00:00",
"window": {
"start": "09:00",
"end": "18:00",
"days": [
"string"
],
"timezone": "string"
},
"concurrency": 1,
"max_attempts": 1,
"retry_minutes": 1,
"instructions": "string",
"progress": {
"total": 1,
"pending": 1,
"completed": 1,
"no_answer": 1,
"failed": 1,
"skipped": 1
},
"created_at": "2026-09-13T14:02:11+00:00"
}
}
Company
Business profile, services, FAQs, staff and the knowledge base, and the questions the assistant could not answer.
/business
— Get company profile
scope: read
Everything assistants know about the business: details, hours, holidays, booking rules, policies, services, FAQs, staff and locations.
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/business \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/business', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/business'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/business", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "business",
"name": "string",
"industry": "string",
"description": "string",
"website": "string",
"email": "string",
"phone": "string",
"address": {
"line1": "string",
"line2": "string",
"city": "string",
"state": "string",
"postal_code": "string",
"country": "string"
},
"timezone": "string",
"opening_hours": {
"mon": [
[
"09:00",
"17:00"
]
],
"sat": []
},
"holidays": [
{
"date": "string",
"name": "string"
}
],
"booking_rules": {
"slot_minutes": 1,
"buffer_minutes": 1,
"lead_time_hours": 1,
"horizon_days": 1,
"allow_same_day": true
},
"policies": "string",
"ai_summary": "string",
"services": [
{
"id": 1,
"object": "service",
"name": "string",
"description": "string",
"price_cents": 1,
"duration_minutes": 1,
"bookable": true,
"active": true,
"payment_link": "string"
}
],
"faqs": [
{
"id": 1,
"object": "faq",
"question": "string",
"answer": "string",
"active": true
}
],
"staff": [
{
"id": 1,
"object": "staff_member",
"name": "string",
"role": "string",
"phone": "string",
"email": "string",
"transferable": true,
"bookable": true,
"active": true
}
],
"locations": [
{
"id": 1,
"name": "string",
"address": "string",
"phone": "string",
"is_primary": true
}
]
}
}
/business
— Update company profile
scope: write
Partial update. opening_hours is an object keyed mon..sun with arrays of ["HH:MM","HH:MM"] ranges.
Body (BusinessInput)
| name | string | |
| industry | string | dental | medical | veterinary | legal | real_estate | home_services | automotive | beauty | fitness | hospitality | restaurant | retail | insurance | finance | education | agency | software | logistics | nonprofit | other |
| description | string | |
| website | string | |
| string | ||
| phone | string | |
| address | object | |
| timezone | string | |
| opening_hours | object | |
| holidays | array | |
| booking_rules | object | |
| policies | string | |
| ai_summary | string |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/business \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"string","industry":"dental","description":"string","website":"string","email":"string","phone":"string","address":{"line1":"string","line2":"string","city":"string","state":"string","postal_code":"string","country":"string"},"timezone":"string","opening_hours":{},"holidays":[{}],"booking_rules":{},"policies":"string","ai_summary":"string"}'const res = await fetch('https://ringhum.com/api/v1/business', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "string", "industry": "dental", "description": "string", "website": "string", "email": "string", "phone": "string", "address": { "line1": "string", "line2": "string", "city": "string", "state": "string", "postal_code": "string", "country": "string" }, "timezone": "string", "opening_hours": {}, "holidays": [ {} ], "booking_rules": {}, "policies": "string", "ai_summary": "string" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/business', array ( 'name' => 'string', 'industry' => 'dental', 'description' => 'string', 'website' => 'string', 'email' => 'string', 'phone' => 'string', 'address' => array ( 'line1' => 'string', 'line2' => 'string', 'city' => 'string', 'state' => 'string', 'postal_code' => 'string', 'country' => 'string', ), 'timezone' => 'string', 'opening_hours' => (object) array( ), 'holidays' => array ( 0 => (object) array( ), ), 'booking_rules' => (object) array( ), 'policies' => 'string', 'ai_summary' => 'string', )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/business", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"string","industry":"dental","description":"string","website":"string","email":"string","phone":"string","address":{"line1":"string","line2":"string","city":"string","state":"string","postal_code":"string","country":"string"},"timezone":"string","opening_hours":{},"holidays":[{}],"booking_rules":{},"policies":"string","ai_summary":"string"}, ) data = r.json()["data"]
{
"data": {
"object": "business",
"name": "string",
"industry": "string",
"description": "string",
"website": "string",
"email": "string",
"phone": "string",
"address": {
"line1": "string",
"line2": "string",
"city": "string",
"state": "string",
"postal_code": "string",
"country": "string"
},
"timezone": "string",
"opening_hours": {
"mon": [
[
"09:00",
"17:00"
]
],
"sat": []
},
"holidays": [
{
"date": "string",
"name": "string"
}
],
"booking_rules": {
"slot_minutes": 1,
"buffer_minutes": 1,
"lead_time_hours": 1,
"horizon_days": 1,
"allow_same_day": true
},
"policies": "string",
"ai_summary": "string",
"services": [
{
"id": 1,
"object": "service",
"name": "string",
"description": "string",
"price_cents": 1,
"duration_minutes": 1,
"bookable": true,
"active": true,
"payment_link": "string"
}
],
"faqs": [
{
"id": 1,
"object": "faq",
"question": "string",
"answer": "string",
"active": true
}
],
"staff": [
{
"id": 1,
"object": "staff_member",
"name": "string",
"role": "string",
"phone": "string",
"email": "string",
"transferable": true,
"bookable": true,
"active": true
}
],
"locations": [
{
"id": 1,
"name": "string",
"address": "string",
"phone": "string",
"is_primary": true
}
]
}
}
/business/services
— Add service
scope: write
Body (ServiceInput)
| name* | string | |
| description | string | |
| price_cents | integer | |
| duration_minutes | integer | |
| bookable | boolean | |
| active | boolean | |
| payment_link | string|null | https link (Stripe Payment Link, PayPal.me…) sent with this service's booking confirmation. |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/business/services \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"Teeth cleaning","price_cents":9500,"duration_minutes":45}'const res = await fetch('https://ringhum.com/api/v1/business/services', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "Teeth cleaning", "price_cents": 9500, "duration_minutes": 45 }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/business/services', array ( 'name' => 'Teeth cleaning', 'price_cents' => 9500, 'duration_minutes' => 45, )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/business/services", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"Teeth cleaning","price_cents":9500,"duration_minutes":45}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "service",
"name": "string",
"description": "string",
"price_cents": 1,
"duration_minutes": 1,
"bookable": true,
"active": true,
"payment_link": "string"
}
}
/business/services/{id}
— Update service
scope: write
Change a service, including its own payment_link.
Parameters
| idpath | integer | Service id (required) |
Body (ServiceUpdate)
| name | string | |
| description | string|null | |
| price_cents | integer|null | |
| duration_minutes | integer | |
| bookable | boolean | |
| active | boolean | |
| payment_link | string|null |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/business/services/1 \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"string","description":"string","price_cents":1,"duration_minutes":1,"bookable":true,"active":true,"payment_link":"string"}'const res = await fetch('https://ringhum.com/api/v1/business/services/1', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "string", "description": "string", "price_cents": 1, "duration_minutes": 1, "bookable": true, "active": true, "payment_link": "string" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/business/services/1', array ( 'name' => 'string', 'description' => 'string', 'price_cents' => 1, 'duration_minutes' => 1, 'bookable' => true, 'active' => true, 'payment_link' => 'string', )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/business/services/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"string","description":"string","price_cents":1,"duration_minutes":1,"bookable":true,"active":true,"payment_link":"string"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "service",
"name": "string",
"description": "string",
"price_cents": 1,
"duration_minutes": 1,
"bookable": true,
"active": true,
"payment_link": "string"
}
}
/business/services/{id}
— Delete service
scope: write
Parameters
| idpath | integer | Service id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/business/services/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/business/services/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/business/services/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/business/services/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "string",
"deleted": true
}
}
/business/faqs
— Add FAQ
scope: write
Body (FaqInput)
| question* | string | |
| answer* | string | |
| active | boolean |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/business/faqs \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"question":"Do you accept insurance?","answer":"Yes, most PPO plans."}'const res = await fetch('https://ringhum.com/api/v1/business/faqs', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "question": "Do you accept insurance?", "answer": "Yes, most PPO plans." }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/business/faqs', array ( 'question' => 'Do you accept insurance?', 'answer' => 'Yes, most PPO plans.', )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/business/faqs", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"question":"Do you accept insurance?","answer":"Yes, most PPO plans."}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "faq",
"question": "string",
"answer": "string",
"active": true
}
}
/business/faqs/{id}
— Delete FAQ
scope: write
Parameters
| idpath | integer | FAQ id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/business/faqs/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/business/faqs/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/business/faqs/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/business/faqs/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "string",
"deleted": true
}
}
/business/staff
— Add staff member
scope: write
Staff can be transfer targets and/or bookable.
Body (StaffInput)
| name* | string | |
| role | string | |
| phone | string | E.164 phone number |
| string | ||
| transferable | boolean | |
| bookable | boolean | |
| working_hours | object|null |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/business/staff \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"Dr. Amanda Reyes","role":"Dentist","phone":"+17185550124","bookable":true}'const res = await fetch('https://ringhum.com/api/v1/business/staff', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "Dr. Amanda Reyes", "role": "Dentist", "phone": "+17185550124", "bookable": true }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/business/staff', array ( 'name' => 'Dr. Amanda Reyes', 'role' => 'Dentist', 'phone' => '+17185550124', 'bookable' => true, )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/business/staff", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"name":"Dr. Amanda Reyes","role":"Dentist","phone":"+17185550124","bookable":true}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "staff_member",
"name": "string",
"role": "string",
"phone": "string",
"email": "string",
"transferable": true,
"bookable": true,
"active": true
}
}
/business/staff/{id}
— Delete staff member
scope: write
Parameters
| idpath | integer | Staff member id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/business/staff/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/business/staff/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/business/staff/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/business/staff/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "string",
"deleted": true
}
}
/knowledge
— List knowledge documents
scope: read
Parameters
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/knowledge \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/knowledge', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/knowledge'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/knowledge", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "knowledge_document",
"title": "string",
"source_type": "text",
"source": "string",
"assistant_id": 1,
"status": "pending",
"error": "string",
"chunks_count": 1,
"bytes": 1,
"embedded": true,
"auto_sync": true,
"last_synced_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/knowledge
— Add knowledge document
scope: write
Send title + content, or a url to fetch. Indexing runs in the background; poll status until ready.
Body (KnowledgeInput)
| title | string | |
| content | string | |
| url | string | |
| assistant_id | integer|null | Restrict to one assistant; omit for all. |
| auto_sync | boolean | Re-fetch the URL weekly. |
Responses
- 202OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/knowledge \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"title":"string","content":"string","url":"string","assistant_id":1,"auto_sync":true}'const res = await fetch('https://ringhum.com/api/v1/knowledge', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "title": "string", "content": "string", "url": "string", "assistant_id": 1, "auto_sync": true }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/knowledge', array ( 'title' => 'string', 'content' => 'string', 'url' => 'string', 'assistant_id' => 1, 'auto_sync' => true, )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/knowledge", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"title":"string","content":"string","url":"string","assistant_id":1,"auto_sync":true}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "knowledge_document",
"title": "string",
"source_type": "text",
"source": "string",
"assistant_id": 1,
"status": "pending",
"error": "string",
"chunks_count": 1,
"bytes": 1,
"embedded": true,
"auto_sync": true,
"last_synced_at": "2026-09-13T14:02:11+00:00",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/knowledge/search
— Search knowledge
scope: read
The same search the assistant runs during a call: matching FAQs plus the best document passages.
Parameters
| qquery | string | (required) |
| assistant_idquery | integer | Also search the documents that belong to this assistant only. |
| limitquery | integer |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/knowledge/search \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/knowledge/search', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/knowledge/search'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/knowledge/search", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "knowledge_search",
"query": "string",
"faqs": [
{
"question": "string",
"answer": "string",
"score": 1
}
],
"results": [
{
"document": "string",
"document_id": 1,
"content": "string",
"score": 1
}
]
}
}
/knowledge/{id}
— Delete knowledge document
scope: write
Parameters
| idpath | integer | Document id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/knowledge/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/knowledge/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/knowledge/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/knowledge/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "string",
"deleted": true
}
}
/knowledge/gaps
— List questions it could not answer
scope: read
Questions callers and chats asked that the assistant did not know, grouped by meaning, most asked first.
Parameters
| statusquery | string: open | answered | dismissed | |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/knowledge/gaps \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/knowledge/gaps', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/knowledge/gaps'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/knowledge/gaps", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "knowledge_gap",
"question": "Do you have parking?",
"status": "open",
"times_asked": 1,
"last_asked_at": "2026-09-13T14:02:11+00:00",
"suggested_answer": "string",
"faq_id": 1,
"assistant_id": 1,
"latest_call_id": 1
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/knowledge/gaps/{id}/suggest
— Suggest an answer
scope: write
Drafts an answer from the company information; missing facts come back as [placeholders] to fill in.
Parameters
| idpath | integer | Question id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/knowledge/gaps/1/suggest \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/knowledge/gaps/1/suggest', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/knowledge/gaps/1/suggest'); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/knowledge/gaps/1/suggest", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "knowledge_gap",
"question": "Do you have parking?",
"status": "open",
"times_asked": 1,
"last_asked_at": "2026-09-13T14:02:11+00:00",
"suggested_answer": "string",
"faq_id": 1,
"assistant_id": 1,
"latest_call_id": 1
}
}
/knowledge/gaps/{id}/answer
— Answer as FAQ
scope: write
Saves the answer as an FAQ the assistant uses from the next call on, and closes the question.
Parameters
| idpath | integer | Question id (required) |
Body (KnowledgeGapAnswer)
| answer* | string | The answer the assistant should give. No [placeholders]. |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/knowledge/gaps/1/answer \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"answer":"Yes, we have 12 free parking spaces behind the building."}'const res = await fetch('https://ringhum.com/api/v1/knowledge/gaps/1/answer', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "answer": "Yes, we have 12 free parking spaces behind the building." }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/knowledge/gaps/1/answer', array ( 'answer' => 'Yes, we have 12 free parking spaces behind the building.', )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/knowledge/gaps/1/answer", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"answer":"Yes, we have 12 free parking spaces behind the building."}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "knowledge_gap",
"question": "Do you have parking?",
"status": "open",
"times_asked": 1,
"last_asked_at": "2026-09-13T14:02:11+00:00",
"suggested_answer": "string",
"faq_id": 1,
"assistant_id": 1,
"latest_call_id": 1
}
}
/knowledge/gaps/{id}/dismiss
— Dismiss question
scope: write
Not relevant: hides it.
Parameters
| idpath | integer | Question id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/knowledge/gaps/1/dismiss \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/knowledge/gaps/1/dismiss', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/knowledge/gaps/1/dismiss'); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/knowledge/gaps/1/dismiss", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "knowledge_gap",
"question": "Do you have parking?",
"status": "open",
"times_asked": 1,
"last_asked_at": "2026-09-13T14:02:11+00:00",
"suggested_answer": "string",
"faq_id": 1,
"assistant_id": 1,
"latest_call_id": 1
}
}
Growth
Payment links, Google review requests, and the revenue the assistant brought in.
/business/payments
— Get payment settings
scope: read
The business's own payment link and when it is sent. Ringhum never handles the money.
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/business/payments \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/business/payments', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/business/payments'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/business/payments", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "payment_settings",
"link": "string",
"note": "a $20 deposit",
"bookings": true,
"orders": true
}
}
/business/payments
— Update payment settings
scope: write
Set the link (https: Stripe Payment Link, PayPal.me, Square…), what it is for, and whether it goes out with booking and order confirmations.
Body (PaymentSettingsInput)
| link | string|null | |
| note | string|null | |
| bookings | boolean | |
| orders | boolean |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/business/payments \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"link":"string","note":"string","bookings":true,"orders":true}'const res = await fetch('https://ringhum.com/api/v1/business/payments', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "link": "string", "note": "string", "bookings": true, "orders": true }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/business/payments', array ( 'link' => 'string', 'note' => 'string', 'bookings' => true, 'orders' => true, )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/business/payments", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"link":"string","note":"string","bookings":true,"orders":true}, ) data = r.json()["data"]
{
"data": {
"object": "payment_settings",
"link": "string",
"note": "a $20 deposit",
"bookings": true,
"orders": true
}
}
/business/review-requests
— Get review request settings
scope: read
Whether happy customers are asked for a review after a visit, order or stay, and how many were sent and opened in the last 30 days.
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/business/review-requests \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/business/review-requests', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/business/review-requests'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/business/review-requests", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "review_request_settings",
"enabled": true,
"url": "string",
"delay_hours": 1,
"appointments": true,
"orders": true,
"stays": true,
"skip_unhappy": true,
"message": "string",
"last_30_days": {
"sent": 1,
"opened": 1
}
}
}
/business/review-requests
— Update review request settings
scope: write
Switch on with the review page link (Google, Tripadvisor, Trustpilot…); only visits after switching on are asked about.
Body (ReviewRequestSettingsInput)
| enabled | boolean | |
| url | string|null | |
| delay_hours | integer | |
| appointments | boolean | |
| orders | boolean | |
| stays | boolean | |
| skip_unhappy | boolean | |
| message | string|null | Your own text; :name, :company and :link are filled in. |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/business/review-requests \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"enabled":true,"url":"string","delay_hours":1,"appointments":true,"orders":true,"stays":true,"skip_unhappy":true,"message":"string"}'const res = await fetch('https://ringhum.com/api/v1/business/review-requests', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "enabled": true, "url": "string", "delay_hours": 1, "appointments": true, "orders": true, "stays": true, "skip_unhappy": true, "message": "string" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/business/review-requests', array ( 'enabled' => true, 'url' => 'string', 'delay_hours' => 1, 'appointments' => true, 'orders' => true, 'stays' => true, 'skip_unhappy' => true, 'message' => 'string', )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/business/review-requests", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"enabled":true,"url":"string","delay_hours":1,"appointments":true,"orders":true,"stays":true,"skip_unhappy":true,"message":"string"}, ) data = r.json()["data"]
{
"data": {
"object": "review_request_settings",
"enabled": true,
"url": "string",
"delay_hours": 1,
"appointments": true,
"orders": true,
"stays": true,
"skip_unhappy": true,
"message": "string",
"last_30_days": {
"sent": 1,
"opened": 1
}
}
}
/review-requests
— List review requests
scope: read
Review requests sent to customers, newest first.
Parameters
| statusquery | string: sent | failed | opened | |
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/review-requests \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/review-requests', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/review-requests'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/review-requests", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "review_request",
"status": "pending",
"channel": "sms",
"recipient": "string",
"customer_name": "string",
"subject": {
"type": "string",
"id": 1
},
"contact_id": 1,
"sent_at": "2026-09-13T14:02:11+00:00",
"opened_at": "2026-09-13T14:02:11+00:00",
"error": "string"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/revenue
— Get revenue from the assistant
scope: read
Bookings (at their service price), orders and reservations the assistant took itself, minus cancellations and no-shows, per currency.
Parameters
| daysquery | integer |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/revenue \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/revenue', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/revenue'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/revenue", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "assistant_revenue",
"days": 1,
"totals_cents": {
"USD": 53250
},
"formatted": "$532.50",
"appointments": {},
"orders": {},
"reservations": {}
}
}
Spam protection
Refusing robocalls, cold calls and blocked numbers before anyone answers.
/spam-protection
— Get spam protection
scope: read
Settings and how many calls were refused in the last 30 days.
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/spam-protection \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/spam-protection', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/spam-protection'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/spam-protection", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"object": "spam_protection",
"enabled": true,
"block_hidden": true,
"refused_last_30_days": 1,
"blocked_numbers": 1
}
}
/spam-protection
— Update spam protection
scope: write
enabled: refuse numbers flagged by several businesses and hang up on robocalls; block_hidden: refuse callers who hide their number.
Body (SpamProtectionInput)
| enabled | boolean | |
| block_hidden | boolean |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 429More than 120 requests in a minute.
curl -X PATCH https://ringhum.com/api/v1/spam-protection \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"enabled":true,"block_hidden":true}'const res = await fetch('https://ringhum.com/api/v1/spam-protection', { method: 'PATCH', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "enabled": true, "block_hidden": true }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->patch('https://ringhum.com/api/v1/spam-protection', array ( 'enabled' => true, 'block_hidden' => true, )); $data = $response->json('data');import os, requests r = requests.patch( "https://ringhum.com/api/v1/spam-protection", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"enabled":true,"block_hidden":true}, ) data = r.json()["data"]
{
"data": {
"object": "spam_protection",
"enabled": true,
"block_hidden": true,
"refused_last_30_days": 1,
"blocked_numbers": 1
}
}
/blocked-numbers
— List blocked numbers
scope: read
Parameters
| per_pagequery | integer | Items per page. |
| pagequery | integer | Page number. |
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/blocked-numbers \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/blocked-numbers', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/blocked-numbers'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/blocked-numbers", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"id": 1,
"object": "blocked_number",
"phone": "+14155550132",
"note": "string",
"created_at": "2026-09-13T14:02:11+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 1,
"total": 1,
"next_page_url": "string",
"prev_page_url": "string"
}
}
/blocked-numbers
— Block a number
scope: write
Calls from it are refused before anyone answers and use no minutes.
Body (BlockedNumberInput)
| phone* | string | E.164 phone number |
| note | string|null |
Responses
- 201OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 422The request body or query failed validation.
- 429More than 120 requests in a minute.
curl -X POST https://ringhum.com/api/v1/blocked-numbers \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"phone":"+14155550166","note":"Robocall"}'const res = await fetch('https://ringhum.com/api/v1/blocked-numbers', { method: 'POST', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "phone": "+14155550166", "note": "Robocall" }), }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->post('https://ringhum.com/api/v1/blocked-numbers', array ( 'phone' => '+14155550166', 'note' => 'Robocall', )); $data = $response->json('data');import os, requests r = requests.post( "https://ringhum.com/api/v1/blocked-numbers", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, json={"phone":"+14155550166","note":"Robocall"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "blocked_number",
"phone": "+14155550132",
"note": "string",
"created_at": "2026-09-13T14:02:11+00:00"
}
}
/blocked-numbers/{id}
— Unblock a number
scope: write
Parameters
| idpath | integer | Blocked number id (required) |
Responses
- 200OK
- 401Missing or invalid API key.
- 403The key lacks the required scope.
- 404No such resource in this workspace.
- 429More than 120 requests in a minute.
curl -X DELETE https://ringhum.com/api/v1/blocked-numbers/1 \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/blocked-numbers/1', { method: 'DELETE', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->delete('https://ringhum.com/api/v1/blocked-numbers/1'); $data = $response->json('data');import os, requests r = requests.delete( "https://ringhum.com/api/v1/blocked-numbers/1", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"object": "string",
"deleted": true
}
}
Catalog
Voices, languages, models and event names.
/voices
— List voices
scope: read
Voices you can assign to an assistant (voice_id).
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/voices \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/voices', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/voices'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/voices", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"object": "voice",
"id": "string",
"name": "string",
"gender": "string",
"language": "string",
"style": "string"
}
]
}
/languages
— List languages
scope: read
Language codes accepted by language.
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/languages \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/languages', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/languages'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/languages", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"object": "language",
"code": "string",
"name": "string"
}
]
}
/events
— List webhook events
scope: read
Event names you can subscribe to.
Responses
- 200OK
- 401Missing or invalid API key.
- 429More than 120 requests in a minute.
curl -X GET https://ringhum.com/api/v1/events \ -H "Authorization: Bearer ck_live_…"const res = await fetch('https://ringhum.com/api/v1/events', { method: 'GET', headers: { Authorization: 'Bearer ' + process.env.RINGHUM_API_KEY }, }); const { data } = await res.json();$response = Http::withToken(env('RINGHUM_API_KEY')) ->get('https://ringhum.com/api/v1/events'); $data = $response->json('data');import os, requests r = requests.get( "https://ringhum.com/api/v1/events", headers={"Authorization": f"Bearer {os.environ['RINGHUM_API_KEY']}"}, ) data = r.json()["data"]
{
"data": [
{
"object": "event",
"name": "string",
"description": "string"
}
]
}
OpenAPI and SDKs
The full specification is published at https://ringhum.com/api/v1/openapi.json (OpenAPI 3.1). Import it into Postman or Insomnia, or generate a typed client:
npx @openapitools/openapi-generator-cli generate -i https://ringhum.com/api/v1/openapi.json -g typescript-fetch -o ./ringhum-sdk
# or: -g php, -g python, -g go, -g csharp …