HiThisIs your AI receptionist

Reseller API

Offer the front desk inside your own product. This API runs the whole lifecycle for your customers: create a client business, configure how the front desk answers, buy a phone number, set a plan, and pull every call and lead. Everything the dashboard does, you can do here.

Introduction

The API is JSON over HTTPS. You authenticate as a reseller with a single bearer key; every request is automatically scoped to your own account, so you can only ever see or change your own clients. There is no SDK to install: the examples below are plain curl and map directly to any HTTP client.

Base URL & versioning

https://hithisis.com/api/reseller/v1

All paths below are relative to this base. The version is pinned in the path (v1); breaking changes ship under a new version, so your integration keeps working.

Authentication

Send your reseller key as a bearer token on every request. Generate and rotate it in your dashboard under API keys. Keep it server-side, never in a browser or mobile app: it can create clients and buy numbers.

Authorization: Bearer htir_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Rotating or revoking the key stops the old one immediately. A missing or bad key returns 401; a suspended reseller account returns 403.

Conventions

  • Send Content-Type: application/json on POST, PUT, and PATCH.
  • Timestamps are Unix epoch seconds (integers).
  • Phone numbers are E.164, e.g. +19735551234.
  • Money is either whole US dollars (plan prices) or integer cents (spend_cap_cents), as noted per field.
  • Booleans accept true/false.
  • PATCH is a partial update: send only the fields you want to change.

Rate limits

120 requests per minute per key. Over the limit returns 429. If you need a higher limit for a bulk import, contact us. Space out large batches and retry 429 responses after a short backoff.

Errors

Errors return the HTTP status plus a JSON body {"detail": "what went wrong"}.

StatusMeaning
200 / 201Success
400Malformed request (bad JSON)
401Missing or invalid API key
403Reseller account not active
404Client, call, or number not found (or not yours)
409Conflict (e.g. no Twilio connected, or client already has a number)
422Validation error (a field value was rejected)
429Rate limit exceeded
502Upstream (Twilio) error

Quickstart

Create a client, give it a number, and you're live:

# 1. Create a client business
curl -X POST https://hithisis.com/api/reseller/v1/clients \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"name":"Ace Plumbing","industry":"plumbing","voice_persona":"sage","notify_email":"owner@ace.com"}'
# -> 201 { "id": "cust_...", ... }

# 2. Find an available number
curl "https://hithisis.com/api/reseller/v1/numbers/available?country=US&area_code=973" \
  -H "Authorization: Bearer $KEY"

# 3. Buy it for the client (the answering webhook is wired automatically)
curl -X POST https://hithisis.com/api/reseller/v1/clients/cust_.../number \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"phone_number":"+19735551234"}'

Reference data GET/meta

Returns the valid values for the fields below (voices, languages, industries, transfer triggers, minute policies, plans, packs), straight from the live system. Build your dropdowns from this so you never hard-code a value that changes.

{
  "voices": [{ "id": "sage", "label": "Sage", "desc": "Warm, steady, female" }, ...],
  "languages": [{ "name": "Hindi", "code": "hi", "spoken": true }, ...],
  "industries": [{ "slug": "plumbing", "label": "Plumbing" }, ...],
  "transfer_triggers": ["always_offer", "on_emergency", "on_request"],
  "minute_policies": ["hard_cap", "overage"],
  "plans": [{ "tier": "pro", "name": "Pro", "price_usd": 199, "minutes": 1200 }, ...],
  "minute_packs": [{ "id": "small", "minutes": 250, "price_usd": 35 }, ...]
}

Configuration fields

These are the settings on a client, set at create time or via PATCH /clients/{id}.

FieldTypeNotes
receptionist_namestringName the front desk gives. Default "Sam".
industrystringAn industry slug (see Industries). Default "general".
voice_personastringOne of the voices.
languagestringA supported language name (see Languages). Default "English".
custom_greetingstring / nullOverride the opening line.
custom_instructionsstring / nullExtra guidance for the front desk.
record_callsbooleanDefault true.
recording_noticebooleanPlay a recording notice. Default false.
after_hours_onlybooleanOnly answer outside business hours.
blocked_numbersstring / nullComma-separated E.164 numbers to reject.
transfer_enabledbooleanNeeds transfer_number set, or it stays off.
transfer_numberstring / nullE.164 number to transfer to.
transfer_triggerstringon_request, on_emergency, or always_offer.
quote_pricesbooleanNeeds pricing_notes set, or it stays off.
pricing_notesstring / nullThe price list the front desk may quote from.
notify_emailstringWhere lead emails go.
notify_smsstring / nullE.164 number for lead texts.
minute_policystringoverage (keep answering, bill overage) or hard_cap (stop at zero).
spend_cap_centsintegerMonthly overage cap in cents. 0 = no cap.

Voices

ValueDescription
sageWarm, steady, female
ashWarm, easy, male
coralBright, friendly, female
verseCalm, even, neutral

Languages (42)

Set with the language field. Understanding the caller is broad across every language below; the spoken voice is strongest where "Spoken" says strong (Hindi and English are the most natural for India). Pilot others before offering them as native quality.

LanguageCodeSpoken
Englishenstrong
Spanishesstrong
Frenchfrstrong
Germandestrong
Italianitstrong
Portugueseptstrong
Dutchnlunderstood, varies
Polishplunderstood, varies
Russianruunderstood, varies
Ukrainianukunderstood, varies
Arabicarstrong
Hindihistrong
Urduurunderstood, varies
Punjabipaunderstood, varies
Bengalibnstrong
Tamiltastrong
Telugutestrong
Marathimrunderstood, varies
Gujaratiguunderstood, varies
Kannadaknunderstood, varies
Malayalammlunderstood, varies
Odiaorunderstood, varies
Assameseasunderstood, varies
Nepalineunderstood, varies
Tagalogtlunderstood, varies
Vietnameseviunderstood, varies
Thaithunderstood, varies
Indonesianidunderstood, varies
Malaymsunderstood, varies
Turkishtrunderstood, varies
Greekelunderstood, varies
Hebrewheunderstood, varies
Swedishsvunderstood, varies
Norwegiannounderstood, varies
Danishdaunderstood, varies
Finnishfiunderstood, varies
Czechcsunderstood, varies
Romanianrounderstood, varies
Hungarianhuunderstood, varies
Japanesejastrong
Koreankounderstood, varies
Chinesezhstrong

Industries (44)

Use the slug. The field also accepts a trade we don't list (it falls back to a general persona). Full list at GET /meta. Examples: hvac, plumbing, roofing, electrical, landscaping, painting, general-contracting, handyman, fencing, concrete, flooring, cleaning, pest-control, garage-door ...

Plans & packs

TierPriceMinutes/mo
starter$99500
pro$1991,200
business$3992,800
scale$6996,000

One-time minute packs (grant with POST /clients/{id}/minutes or your own billing): 250 min ($35), 600 min ($75), 1,500 min ($165).

Clients

POST/clients — create a client

Only name is required. email and any configuration field are optional. A client is created unmetered (you bill your own customer); set a platform plan later with PUT /clients/{id}/plan if you want us to meter minutes.

POST /clients
{ "name": "Ace Plumbing", "email": "owner@ace.com", "industry": "plumbing",
  "voice_persona": "sage", "language": "English", "notify_email": "owner@ace.com" }

-> 201
{ "id": "cust_...", "name": "Ace Plumbing", "status": "agency", "plan_tier": "agency",
  "phone_number": null, "config": { ... }, "usage": { ... } }

GET/clients — list your clients

GET/clients/{id} — one client, with usage

{ "id": "cust_...", "name": "Ace Plumbing", "email": "owner@ace.com",
  "industry": "plumbing", "status": "active", "plan_tier": "pro",
  "phone_number": "+19735551234", "created_at": 1751500000,
  "config": { "receptionist_name": "Sam", "voice_persona": "sage", "language": "English",
              "transfer_enabled": false, "minute_policy": "overage", "spend_cap_cents": 0, ... },
  "usage": { "metered": true, "included": 1200, "available": 1110, ... } }

PATCH/clients/{id} — update configuration

Partial update. Two coupled rules are enforced: enabling transfer requires a transfer_number, and enabling quoting requires pricing_notes.

PATCH /clients/cust_...
{ "voice_persona": "ash", "transfer_enabled": true,
  "transfer_number": "+19735550000", "transfer_trigger": "on_emergency" }

POST/clients/{id}/pause  ·  POST/clients/{id}/resume

Pause stops the line answering (status paused). Resume sets it back to active.

DELETE/clients/{id}

Releases the client's number (if any) from your Twilio account and soft-deletes the client (status disabled; call history is preserved). Returns { "number_released": true|false }.

Plans, usage & minutes

PUT/clients/{id}/plan

Body: {"tier":"pro"} (a platform plan that meters minutes), or {"minutes_included":2000} for a custom allotment, or {"tier":"agency"} to keep it unmetered (you bill the customer yourself).

GET/clients/{id}/usage

{ "metered": true, "included": 1200, "rollover": 0, "addon": 250,
  "used": 340, "overage": 0, "available": 1110,
  "period_start": 1751500000, "reset_at": 1754092000,
  "policy": "overage", "spend_cap_cents": 0 }

POST/clients/{id}/minutes

Grant top-up minutes to the current period. Body: {"minutes":500}.

Phone numbers

Numbers are searched and bought in the Twilio account you connect under API keys. You own the number and its cost. Without a connected Twilio account these return 409.

GET/numbers/available

Query: country (2-letter ISO, default US), area_code, contains, sms (true to require SMS), limit (max 50).

GET /numbers/available?country=US&area_code=973
-> { "country": "US", "numbers": [
     { "phone_number": "+19735550111", "locality": "Newark", "region": "NJ",
       "capabilities": { "voice": true, "sms": true, "mms": true } }, ... ] }

POST/clients/{id}/number

Body: phone_number (exact, from the search) or area_code (we pick one). The answering webhook is wired to us automatically.

India (+91) and other regulated countries. Search with country=IN. Indian numbers require a Twilio-approved regulatory bundle and address (a one-time KYC step you complete in your Twilio account). Create the Bundle + Address in Twilio, then pass their SIDs:

POST /clients/cust_.../number
{ "phone_number": "+9122XXXXXXXX", "bundle_sid": "BUxxxx", "address_sid": "ADxxxx" }

DELETE/clients/{id}/number

Releases the number from your Twilio account and clears it from the client.

Calls & leads

GET/calls — across all your clients (limit, max 500)

GET/clients/{id}/calls  ·  GET/clients/{id}/calls/{call_id}

{ "id": "call_...", "client_id": "cust_...", "type": "inbound", "status": "captured",
  "caller": { "name": "Dana Reed", "phone": "(973) 555-0111",
              "phone_e164": "+19735550111", "email": null, "address": "12 Oak St" },
  "service": "water heater leak", "urgency": "emergency", "preferred_time": null,
  "duration_seconds": 96, "recording_url": "https://...", "transcript": "...",
  "started_at": 1751500000, "ended_at": 1751500096 }

status: in_progress, captured, abandoned, failed. urgency: emergency, same_day, scheduled, or null.

Lead webhook

Register one HTTPS endpoint and we push every client's captured lead to it in real time.

PUT/webhook  ·  GET/webhook  ·  DELETE/webhook

PUT body {"url":"https://your-crm.com/hooks/leads"} returns a signing secret. GET returns the current URL and secret. DELETE removes it.

Payload

POST (your URL)
X-HiThisIs-Signature: <hex hmac-sha256 of the raw body>

{ "event": "lead.captured", "client_id": "cust_...", "business": "Ace Plumbing",
  "lead": { "name": "Dana Reed", "phone": "+19735550111", "address": "12 Oak St",
            "company": null, "service": "water heater leak",
            "urgency": "emergency", "preferredTime": null },
  "call": { "id": "call_...", "durationSeconds": 96, "recordingUrl": "https://..." } }

Verify the signature

# Python
import hmac, hashlib
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers["X-HiThisIs-Signature"]):
    abort(400)

Respond 2xx quickly. We route by client_id, which is globally unique to your account, so a shared customer email across apps never collides.

Full endpoint list

MethodPathPurpose
GET/metaReference data
GET/accountYour account summary
POST/clientsCreate a client
GET/clientsList clients
GET/clients/{id}Get a client + usage
PATCH/clients/{id}Update configuration
POST/clients/{id}/pausePause answering
POST/clients/{id}/resumeResume answering
DELETE/clients/{id}Release number + soft-delete
PUT/clients/{id}/planSet plan or minutes
GET/clients/{id}/usageMinute balance
POST/clients/{id}/minutesGrant top-up minutes
GET/numbers/availableSearch numbers
POST/clients/{id}/numberBuy + assign a number
DELETE/clients/{id}/numberRelease the number
GET/callsCalls across all clients
GET/clients/{id}/callsA client's calls
GET/clients/{id}/calls/{call_id}One call
GET/webhookGet lead webhook + secret
PUT/webhookSet lead webhook
DELETE/webhookRemove lead webhook