Ringhum.
API reference · v1.0.0

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.

OpenAPI JSON Get an API key

Quickstart

  1. Create a key under Developers → API keys. Keys belong to a workspace and are shown once.
  2. Send it as Authorization: Bearer ck_live_….
  3. 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}}. Use page and per_page (max 100).
  • Timestamps are ISO 8601 in UTC. Phone numbers are E.164 (+14155550132). Ids are integers.
  • Every response carries an X-Request-Id header; include it when contacting support.
  • Updates use PATCH with only the fields you want to change. PUT is 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."] }
  }
}
StatustypeWhen
401authentication_errorMissing, revoked or unknown key
403permission_errorKey lacks the scope
404not_foundResource is not in this workspace
422validation_errorBad body or query; see errors
422plan_limitPlan limit reached (assistants, numbers)
429rate_limit_errorToo many requests; honour Retry-After
502Carrier 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.

CategoryAppsWhat 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.

ToolUsed whenWhat it does
search_knowledgeAny factual questionSearches FAQs, documents and crawled pages; returns passages the model must quote from.
check_availabilityCaller wants a timeFree slots for a service/staff member over the next days, minus every connected calendar's busy time.
book_appointmentCaller picks a slotCreates the appointment, contact and confirmations; writes to Google Calendar; fires appointment.booked.
find_appointments / cancel_appointmentReschedule or cancelLooks up the caller's bookings by number and moves or cancels them with the same availability checks.
take_message / request_callbackA human is needed laterCreates an inbox item (message, callback, lead) with priority and notifies the team and connected apps.
send_smsCaller wants something in writingSends the caller a short message (address, link, confirmation) on WhatsApp if the caller uses it, otherwise by text. Asks for permission first.
get_menuCaller asks what you sell or ordersReads your menu or catalogue (or the part matching what the caller asked for) with prices and options.
quote_orderBefore confirming an orderPrices 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_orderCaller confirms the orderCreates 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_ordersOrder or delivery questionFinds the caller's orders taken on Ringhum and in Shopify / WooCommerce by phone, email or order number, with status, items, total and tracking.
cancel_orderCaller wants to cancelCancels one of the caller's open orders, notifies the customer and team and fires order.cancelled.
save_detailsCaller shares detailsSaves name, email, company, notes and your custom variables to the contact record.
end_callThe conversation is doneSays 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 (or cancelled). 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 fire order.updated and cancellations fire order.cancelled, to your webhooks and to connected apps.
  • Manage the catalogue with /products and orders with /orders. POST /orders/quote prices 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 (or cancelled / 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 or ical_export_url on 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 fire reservation.updated and cancellations fire reservation.cancelled.
  • Manage room types with /rooms and stays with /reservations. GET /reservations/availability returns the free units and the price per room type for a stay without saving anything.

WhatsApp

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

  1. Under Phone numbers, open the number and choose Enable WhatsApp. Give the display name people will see in WhatsApp (your business name).
  2. 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 requested to pending to active.
  3. 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 /calls with channel: 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 as whatsapp_call_permission. Without it the call is refused with a 422.
  • 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-numbers returns a whatsapp object per number: status, display_name, calling, ai_replies and activated_at.
  • GET /messages lists texts and WhatsApp messages (filter with channel=whatsapp); POST /messages sends one, with channel auto, whatsapp or sms. See Messages below.
  • POST /calls accepts channel: "whatsapp"; the preconditions above come back as 422 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.

GET /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.
Request
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"]
Response 200
{
    "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"
        }
    }
}
GET /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.
Request
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"]
Response 200
{
    "data": {
        "id": 1,
        "object": "team",
        "name": "string",
        "slug": "string",
        "timezone": "string",
        "plan": "trial",
        "created_at": "2026-09-13T14:02:11+00:00"
    }
}
GET /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.
Request
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"]
Response 200
{
    "data": [
        {
            "id": 1,
            "object": "member",
            "name": "string",
            "email": "string",
            "role": "owner",
            "joined_at": "2026-09-13T14:02:11+00:00"
        }
    ]
}
GET /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.
Request
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"]
Response 200
{
    "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.

GET /assistants — List assistants scope: read

Paginated. Archived assistants are excluded unless status=archived.

Parameters

statusquerystring: draft | active | paused | archived
per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
POST /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.
Request
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"]
Response 201
{
    "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"
    }
}
GET /assistants/{id} — Get assistant scope: read

Full configuration including instructions and knowledge base.

Parameters

idpathintegerAssistant id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
PATCH /assistants/{id} — Update assistant scope: write

Partial update; send only the fields you want to change.

Parameters

idpathintegerAssistant 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.
Request
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"]
Response 200
{
    "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"
    }
}
DELETE /assistants/{id} — Delete assistant scope: write

Archives and soft-deletes the assistant; its phone numbers are unassigned. Calls are kept.

Parameters

idpathintegerAssistant 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.
Request
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"]
Response 200
{
    "data": {
        "id": 1,
        "object": "string",
        "deleted": true
    }
}

Phone numbers

Numbers your assistants answer on.

GET /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.
Request
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"]
Response 200
{
    "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"
        }
    ]
}
GET /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

idpathintegerPhone 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.
Request
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"]
Response 200
{
    "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"
    }
}
PATCH /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

idpathintegerPhone 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
email 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.
Request
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"]
Response 200
{
    "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"
    }
}
GET /phone-numbers/{id} — Get phone number scope: read

Parameters

idpathintegerPhone number id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
PATCH /phone-numbers/{id} — Update phone number scope: write

Assign an assistant (assistant_id, or null to unassign) or rename it.

Parameters

idpathintegerPhone 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.
Request
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"]
Response 200
{
    "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.

GET /calls — List calls scope: read

Newest first. Filter by assistant, status, direction, phone number and date range.

Parameters

assistant_idqueryinteger
statusquerystring: queued | ringing | in_progress | completed | busy | failed | no_answer | canceled
directionquerystring: inbound | outbound
phonequerystringE.164 number that called or was called.
fromquerystringStarted at or after.
toquerystringStarted at or before.
per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
POST /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.
Request
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"]
Response 201
{
    "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"
    }
}
GET /calls/{id} — Get call scope: read

Includes transcript, conversation metrics and metadata.

Parameters

idpathintegerCall id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
            }
        ]
    }
}
DELETE /calls/{id} — Delete call scope: write

Deletes the call, its transcript and recording.

Parameters

idpathintegerCall 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.
Request
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"]
Response 200
{
    "data": {
        "id": 1,
        "object": "string",
        "deleted": true
    }
}
GET /calls/{id}/transcript — Get transcript scope: read

Just the conversation turns, in order.

Parameters

idpathintegerCall id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "data": [
        {
            "role": "user",
            "content": "string",
            "spoken_at": "2026-09-13T14:02:11+00:00"
        }
    ]
}
GET /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

idpathintegerCall id (required)

Responses

  • 200WAV audio
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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.

GET /webhooks — List webhooks scope: read

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
        }
    ]
}
POST /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.
Request
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"]
Response 201
{
    "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_…"
    }
}
PATCH /webhooks/{id} — Update webhook scope: write

Change the URL, the events or pause it with active: false.

Parameters

idpathintegerWebhook 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.
Request
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"]
Response 200
{
    "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"
    }
}
DELETE /webhooks/{id} — Delete webhook scope: write

Parameters

idpathintegerWebhook 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.
Request
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"]
Response 200
{
    "data": {
        "id": 1,
        "object": "string",
        "deleted": true
    }
}
POST /webhooks/{id}/test — Send test event scope: write

Delivers a test.ping event synchronously and reports the HTTP status your endpoint returned.

Parameters

idpathintegerWebhook id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 403The key lacks the required scope.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "data": {
        "delivered": true,
        "status": 1
    }
}

Contacts

Callers and customers, recognised on repeat calls.

GET /contacts — List contacts scope: read

Newest activity first. Filter with q (name, phone, email, company), tag, phone, email or do_not_call.

Parameters

qquerystring
tagquerystring
phonequerystring
do_not_callqueryboolean
per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
POST /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
email 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.
Request
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"]
Response 201
{
    "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": {}
    }
}
POST /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.
Request
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"]
Response 200
{
    "data": {
        "object": "import_result",
        "created": 1,
        "updated": 1,
        "skipped": 1
    }
}
GET /contacts/{id} — Get contact scope: read

Includes notes and custom fields collected during calls.

Parameters

idpathintegerContact id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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": {}
    }
}
PATCH /contacts/{id} — Update contact scope: write

Parameters

idpathintegerContact id (required)

Body (ContactInput)

name string
phone string E.164 phone number
email 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.
Request
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"]
Response 200
{
    "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": {}
    }
}
DELETE /contacts/{id} — Delete contact scope: write

Soft-deletes the contact; calls and appointments are kept.

Parameters

idpathintegerContact 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.
Request
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"]
Response 200
{
    "data": {
        "id": 1,
        "object": "string",
        "deleted": true
    }
}

Appointments

Availability and bookings made by assistants or you.

GET /appointments/availability — Get availability scope: read

Open slots computed from opening hours, holidays, booking rules, staff working hours and existing bookings.

Parameters

datequerystringFirst day to look at (workspace timezone). Defaults to today.
daysqueryinteger
service_idqueryinteger
staff_member_idqueryinteger
duration_minutesqueryinteger

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "data": {
        "object": "availability",
        "timezone": "string",
        "duration_minutes": 1,
        "days": {}
    }
}
GET /appointments — List appointments scope: read

Ordered by start time. Filter with status, from, to, contact_id or upcoming=true.

Parameters

statusquerystring: scheduled | confirmed | completed | cancelled | no_show
fromquerystring
toquerystring
upcomingqueryboolean
per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
POST /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.
Request
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"]
Response 201
{
    "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"
    }
}
GET /appointments/{id} — Get appointment scope: read

Parameters

idpathintegerAppointment id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
PATCH /appointments/{id} — Update appointment scope: write

Send starts_at to reschedule (customer is notified), or status, notes, title.

Parameters

idpathintegerAppointment 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.
Request
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"]
Response 200
{
    "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"
    }
}
DELETE /appointments/{id} — Cancel appointment scope: write

Marks it cancelled and notifies the customer. Optional reason and notify query parameters.

Parameters

idpathintegerAppointment id (required)
reasonquerystring
notifyqueryboolean

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.
Request
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"]
Response 200
{
    "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.

GET /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

statusquerystringOne or more of new, confirmed, preparing, ready, out_for_delivery, completed, cancelled, comma-separated.
fulfillmentquerystring: pickup | delivery | dine_in
fromquerystring
toquerystring
contact_idqueryinteger
openqueryboolean
qquerystring
per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 403Orders are switched off for this workspace
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
POST /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.
Request
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"]
Response 201
{
    "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"
    }
}
POST /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.
Request
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"]
Response 200
{
    "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"
        ]
    }
}
GET /orders/{id} — Get order scope: read

Parameters

idpathintegerOrder id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
PATCH /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

idpathintegerOrder 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.
Request
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"]
Response 200
{
    "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"
    }
}
DELETE /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

idpathintegerOrder id (required)
reasonquerystring
notifyqueryboolean

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.
Request
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"]
Response 200
{
    "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.

GET /products — List products scope: read

Your menu or catalogue, ordered by category. Filter with category, available or q.

Parameters

categoryquerystring
availablequeryboolean
qquerystring
per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
POST /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.
Request
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"]
Response 201
{
    "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"
    }
}
GET /products/{id} — Get product scope: read

Parameters

idpathintegerProduct id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
PATCH /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

idpathintegerProduct 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.
Request
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"]
Response 200
{
    "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"
    }
}
DELETE /products/{id} — Delete product scope: write

Removes it from the catalogue. Orders already placed keep their copy of the item.

Parameters

idpathintegerProduct 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.
Request
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"]
Response 200
{
    "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.

GET /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_inquerystring (required)
check_outquerystring (required)
adultsqueryinteger
childrenqueryinteger
room_idqueryinteger

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.
Request
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"]
Response 200
{
    "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
            }
        ]
    }
}
GET /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

statusquerystringOne or more of requested, confirmed, checked_in, checked_out, cancelled, no_show, comma-separated.
room_idqueryinteger
sourcequerystring: ai | manual | api | ical
fromquerystring
toquerystring
contact_idqueryinteger
upcomingqueryboolean
activequeryboolean
qquerystring
per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 403Hotels are switched off for this workspace
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
POST /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.
Request
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"]
Response 201
{
    "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"
    }
}
GET /reservations/{id} — Get reservation scope: read

Parameters

idpathintegerReservation id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
PATCH /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

idpathintegerReservation 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.
Request
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"]
Response 200
{
    "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"
    }
}
DELETE /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

idpathintegerReservation id (required)
reasonquerystring
notifyqueryboolean

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.
Request
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"]
Response 200
{
    "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.

GET /rooms — List rooms scope: read

Your room types in display order. Filter with category, available or q.

Parameters

categoryquerystring
availablequeryboolean
qquerystring
per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
POST /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.
Request
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"]
Response 201
{
    "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"
    }
}
GET /rooms/{id} — Get room scope: read

Parameters

idpathintegerRoom id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
PATCH /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

idpathintegerRoom 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.
Request
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"]
Response 200
{
    "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"
    }
}
DELETE /rooms/{id} — Delete room scope: write

Removes the room type. Reservations already made keep their dates and lose the room.

Parameters

idpathintegerRoom 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.
Request
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"]
Response 200
{
    "data": {
        "id": 1,
        "object": "string",
        "deleted": true
    }
}

Inbox

Messages, callbacks and leads that need a human.

GET /inbox — List inbox items scope: read

Messages, callback requests, leads, voicemails and inbound texts. status=open returns everything not done.

Parameters

statusquerystring: open | in_progress | done
typequerystring: message | callback | lead | voicemail | sms
per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
GET /inbox/{id} — Get inbox item scope: read

Parameters

idpathintegerInbox item id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
PATCH /inbox/{id} — Update inbox item scope: write

Change status, priority or assigned_to (a member id).

Parameters

idpathintegerInbox 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.
Request
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"]
Response 200
{
    "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.

GET /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

channelquerystring: sms | whatsapp
directionquerystring: inbound | outbound
contact_idqueryinteger
fromquerystringCreated at or after.
toquerystringCreated at or before.
qquerystringMatches the body, or a phone number when it contains digits.
per_pagequeryintegerItems per page.
pagequeryintegerPage 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.
Request
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"]
Response 200
{
    "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"
    }
}
POST /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.
Request
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"]
Response 201
{
    "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"
    }
}
GET /messages/{id} — Get message scope: read

Parameters

idpathintegerMessage id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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.

GET /campaigns — List campaigns scope: read

Parameters

per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
POST /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.
Request
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"]
Response 201
{
    "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"
    }
}
GET /campaigns/{id} — Get campaign scope: read

Includes live progress counts.

Parameters

idpathintegerCampaign id (required)

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 404No such resource in this workspace.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
GET /campaigns/{id}/results — List campaign results scope: read

One row per contact with status, outcome, attempts and the last call id.

Parameters

idpathintegerCampaign id (required)
per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
POST /campaigns/{id}/contacts — Add contacts to campaign scope: write

Contacts already in the campaign are ignored.

Parameters

idpathintegerCampaign 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.
Request
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"]
Response 200
{
    "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"
        }
    }
}
POST /campaigns/{id}/transition — Start, pause or cancel campaign scope: write

Body: {"action": "start" | "pause" | "cancel"}. Calls are only placed inside the calling window.

Parameters

idpathintegerCampaign 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.
Request
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"]
Response 200
{
    "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.

GET /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.
Request
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"]
Response 200
{
    "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
            }
        ]
    }
}
PATCH /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
email 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.
Request
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"]
Response 200
{
    "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
            }
        ]
    }
}
POST /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.
Request
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"]
Response 201
{
    "data": {
        "id": 1,
        "object": "service",
        "name": "string",
        "description": "string",
        "price_cents": 1,
        "duration_minutes": 1,
        "bookable": true,
        "active": true
    }
}
DELETE /business/services/{id} — Delete service scope: write

Parameters

idpathintegerService 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.
Request
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"]
Response 200
{
    "data": {
        "id": 1,
        "object": "string",
        "deleted": true
    }
}
POST /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.
Request
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"]
Response 201
{
    "data": {
        "id": 1,
        "object": "faq",
        "question": "string",
        "answer": "string",
        "active": true
    }
}
DELETE /business/faqs/{id} — Delete FAQ scope: write

Parameters

idpathintegerFAQ 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.
Request
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"]
Response 200
{
    "data": {
        "id": 1,
        "object": "string",
        "deleted": true
    }
}
POST /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
email 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.
Request
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"]
Response 201
{
    "data": {
        "id": 1,
        "object": "staff_member",
        "name": "string",
        "role": "string",
        "phone": "string",
        "email": "string",
        "transferable": true,
        "bookable": true,
        "active": true
    }
}
DELETE /business/staff/{id} — Delete staff member scope: write

Parameters

idpathintegerStaff 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.
Request
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"]
Response 200
{
    "data": {
        "id": 1,
        "object": "string",
        "deleted": true
    }
}
GET /knowledge — List knowledge documents scope: read

Parameters

per_pagequeryintegerItems per page.
pagequeryintegerPage number.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "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"
    }
}
POST /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.
Request
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"]
Response 202
{
    "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"
    }
}
GET /knowledge/search — Search knowledge scope: read

The same search the assistant runs during a call: matching FAQs plus the best document passages.

Parameters

qquerystring (required)
assistant_idqueryinteger
limitqueryinteger

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "data": {
        "object": "knowledge_search",
        "query": "string",
        "faqs": [
            {
                "question": "string",
                "answer": "string",
                "score": 1
            }
        ],
        "results": [
            {
                "document": "string",
                "document_id": 1,
                "content": "string",
                "score": 1
            }
        ]
    }
}
DELETE /knowledge/{id} — Delete knowledge document scope: write

Parameters

idpathintegerDocument 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.
Request
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"]
Response 200
{
    "data": {
        "id": 1,
        "object": "string",
        "deleted": true
    }
}

Catalog

Voices, languages, models and event names.

GET /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.
Request
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"]
Response 200
{
    "data": [
        {
            "object": "voice",
            "id": "string",
            "name": "string",
            "gender": "string",
            "language": "string",
            "style": "string"
        }
    ]
}
GET /languages — List languages scope: read

Language codes accepted by language.

Responses

  • 200OK
  • 401Missing or invalid API key.
  • 429More than 120 requests in a minute.
Request
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"]
Response 200
{
    "data": [
        {
            "object": "language",
            "code": "string",
            "name": "string"
        }
    ]
}
GET /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.
Request
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"]
Response 200
{
    "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 …