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"}'
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 you send counts as about 0.13 of a minute (its 1¢ price over your plan's per-minute rate). Messages you receive are free. Past your minutes a message you send 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,
"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 | |
| 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,
"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,
"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 | |
| 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,
"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
}
}
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.",
"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.",
"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.",
"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}
— 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,
"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,
"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,
"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)
| 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 '{"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({ "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 ( '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={"starts_at":"2026-09-13T14:02:11+00:00","status":"scheduled","notes":"string","title":"string","notify":true}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"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,
"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,
"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,
"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,
"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)
| 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 '{"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({ "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 ( '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={"status":"new","notes":"string","requested_for":"2026-09-13T14:02:11+00:00","notify":true}, ) data = r.json()["data"]
{
"data": {
"id": 1,
"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,
"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":"[email protected]","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": "[email protected]", "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' => '[email protected]', '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":"[email protected]","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 | |
| 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 |
| body* | string | |
| channel | string | Optional; `auto` by default (WhatsApp inside the 24-hour window, otherwise SMS). auto | whatsapp | sms |
| 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.
/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
}
],
"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
}
],
"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 |
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
}
}
/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 | |
| 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
}
}
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 …