Skip to content

🔌 Documentation: API

Integration with your CRM/systems. What it is, how it works and how to use it — explained step by step.

🔌 API TelHarbor

Integrate SMS, WhatsApp, Click-to-dial, call data and webhooks (screen-pop) into your systems. Base: https://api.telharbor.com/v1

💬
SMSSend text messages. Scopes: sms.send, sms.status
🟢
WhatsAppSend WhatsApp messages. Scopes: whatsapp.send, whatsapp.status
📞
Click-to-dialOriginate calls on the client PBX. Scope: dial.call

Each API key is enabled by TelHarbor with the contracted service(s). Request your key from the support team.

🤔 What is an API?

API stands for Application Programming Interface. In plain words: it is a doorway that lets your system (your CRM, website or app) ask TelHarbor to do things automatically, without a person doing it by hand.

🧑‍💻You (your system)Wants to send an SMS or make a call
→
🧑‍🍳The API (the waiter)Takes your order and brings the answer
→
🏢TelHarbor (the kitchen)Does the work: sends, calls, logs

It is like a waiter in a restaurant: you do not go into the kitchen; you ask the waiter and they bring what you need. The API is that middleman: you ask (a request) and get a response.

The conversation: request and response

💻 Your application 🏢 API TelHarbor 1. Request "send this SMS to +506…" 2. Response "done, sent ✓"

This works the same for all TelHarbor services: SMS, WhatsApp and Click-to-dial. What you ask and receive changes, but the mechanism (ask with your key → get a response) is the same.

🗺️ How does the TelHarbor API work?

Your system connects only to the TelHarbor API (never directly to the phone network or the PBX). TelHarbor validates your key, applies the security limits and runs the action on the right service.

🖥️ Your system CRM / web / app HTTPS + API key API TelHarbor ✓ Validates the key ✓ Checks allowed IP ✓ Rate limit & quota ✓ Checks permissions (scope) ✓ Logs usage (inbox) UCaaS routes to service 💬 SMS mobile network 🟢 WhatsApp WhatsApp Business 📞 Click-to-dial AMI → your PBX → call

Security: your system never connects directly to the phone system or the providers. Everything goes through the TelHarbor API, which validates and logs every request.

📞 Click-to-dial flow (step by step)

With a single request, TelHarbor first rings the agent's extension and, when they answer, dials the destination. The agent never types the number: they answer their phone and are already calling.

1 Your system POST /v1/dial/call from=7631, to=+506… HTTPS+key 2 API TelHarbor validates & orders originate via AMI AMI 3 Your PBX originates the call ① First it rings the agent 4 📞 Agent (ext 7631) phone rings → answers ② dials 5 👤 Customer (destination) their phone rings 6 🗣️ Conversation

Call center security: you can hide the customer number on the agent screen (they will only see the name assigned to the key). The destination sees the CallerID of your PBX outbound route, not your system.

🔑 Authentication, limits & security

Authenticate every request with your API key in a header:

X-API-Key: <YOUR_API_KEY> # or: Authorization: Bearer <YOUR_API_KEY>
  • IP allowlist: optionally your key is restricted to authorized IPs/CIDR.
  • Rate limit per minute and daily quota per key.
  • Every call is stored in the usage log (audit & billing).
  • Scopes: the key can only use the services assigned to it.

Test connection GET /ping

curl -H "X-API-Key: <YOUR_API_KEY>" https://api.telharbor.com/v1/ping

💬 SMS POST /sms/send

curl -X POST https://api.telharbor.com/v1/sms/send \ -H "X-API-Key: <YOUR_API_KEY>" -H "Content-Type: application/json" \ -d '{"from":"<YOUR_NUMBER>","to":"+50643000000","text":"Hello from TelHarbor"}'

🟢 WhatsApp POST /whatsapp/send

curl -X POST https://api.telharbor.com/v1/whatsapp/send \ -H "X-API-Key: <YOUR_API_KEY>" -H "Content-Type: application/json" \ -d '{"from":"<YOUR_NUMBER>","to":"+50643000000","text":"Hello from TelHarbor"}'

Outside the 24-hour window WhatsApp only delivers approved templates. Send template with the name, language and one value per {{n}}; text becomes optional.

curl -X POST https://api.telharbor.com/v1/whatsapp/send \ -H "X-API-Key: <YOUR_API_KEY>" -H "Content-Type: application/json" \ -d '{"from":"<YOUR_NUMBER>","to":"+50643000000","template":{"name":"your_template","language":"es","params":["value 1","value 2"]}}'

Message status GET /messages?id=123

curl -H "X-API-Key: <YOUR_API_KEY>" "https://api.telharbor.com/v1/messages?id=123"

📞 Click-to-dial POST /dial/call

Rings the agent extension and, upon answer, dials the destination. The client never connects directly to the PBX: TelHarbor originates it securely via AMI.

Request

FieldRequiredDescription
fromYesAgent extension on your PBX (e.g. 7631). It rings first.
toYesDestination number (E.164, e.g. +50643000000) or an internal extension.
caller_idNoCallerID to show. If omitted, the name configured on the key applies.
curl -X POST https://api.telharbor.com/v1/dial/call \ -H "X-API-Key: <YOUR_API_KEY>" -H "Content-Type: application/json" \ -d '{"from":"7631","to":"+50643000000"}'

Response

{"ok":true,"message":"Call originated","call":{"from":"7631","to":"+50643000000","action_id":"thapi_req_..."},"request_id":"req_..."}

The PBX to use is associated with your API key by TelHarbor; it is not sent in the request. A key can only originate on its assigned PBX.

📇 Data: calls, queues & recordings

Read-only endpoints. Each API key sees only the resources in its scope (the extensions and queue TelHarbor assigns it); requesting anything else returns 403/404.

Call records (CDR) GET /v1/calls

List of calls by date range. A call may appear in several rows sharing the same call_id (the linkedid), which is used to fetch its recording.

curl "https://api.telharbor.com/v1/calls?date_from=2026-09-01&limit=50" \ -H "X-API-Key: <YOUR_API_KEY>"

Params: date_from, date_to (YYYY-MM-DD[ HH:MM:SS]), extension, limit (max 200), offset. Scope: calls.read.

Queue status GET /v1/queues/status?queue=NNNN

Instant snapshot of the queue: waiting calls, service level and agents with their status and pause.

curl "https://api.telharbor.com/v1/queues/status?queue=7632" \ -H "X-API-Key: <YOUR_API_KEY>"

Returns waiting, agents_available, service_level_pct and agents[]. Scope: queues.read.

Recordings GET /v1/recordings/get?call_id=<linkedid>

Streams the audio of a call in your scope. Returns audio/wav with HTTP Range support; the audio is not stored on our side. A foreign call_id returns 404.

curl "https://api.telharbor.com/v1/recordings/get?call_id=call_EJEMPLO…" \ -H "X-API-Key: <YOUR_API_KEY>" --output call.wav

Scope: recordings.read.

🔔 Webhooks — real-time events (screen-pop)

Instead of you polling, TelHarbor notifies you: for each call event we send a POST to a URL of yours. Ideal for screen-pop (open the customer record when the call arrives) and to keep the dashboard live.

How it is enabled

  1. You give us a public https URL (with a valid certificate) that accepts POST.
  2. TelHarbor registers it and hands you a secret (whsec_…) to verify the signature.
  3. From then on you receive your call events, in near real time.

Events

EventWhen
call.queuedThe call entered the queue.
call.ringingRinging at the agent → open the record (screen-pop).
call.answeredThe agent answered.
call.completedEnded (includes duration).
call.missedAbandoned / unanswered.

There are also message.inbound and message.status (SMS/WhatsApp/Email) for messaging users.

Event example

POST (to your URL)Content-Type: application/json X-TH-Signature: t=1789340576,v1=EJEMPLO… {"id":"evt_5f01...","type":"call.ringing","api_version":"v1", "created_at":"2026-09-13T23:02:53Z", "data":{"call_id":"call_EJEMPLO…","queue":"7632","direction":"inbound", "agent_extension":"7633","caller_number":"+50643000000","did":"+18887317044", "wait_seconds":0,"event_at":"2026-09-13T23:02:53Z"}}

Verify the signature (HMAC) — required

The header is X-TH-Signature: t=<unix>,v1=<hmac>. Recompute HMAC-SHA256(secret, "<t>.<raw_body>") and compare with v1. Reject if it does not match or if t is older than ~5 min (anti-replay). Use the raw body, without re-serializing.

// PHP$raw = file_get_contents('php://input'); preg_match('/t=(\d+),v1=([a-f0-9]+)/', $_SERVER['HTTP_X_TH_SIGNATURE'] ?? '', $m); $mine = hash_hmac('sha256', $m[1] . '.' . $raw, $SECRET); if (!hash_equals($mine, $m[2] ?? '') || time() - (int)$m[1] > 300) { http_response_code(400); exit; } $evt = json_decode($raw, true); // $evt['type'], $evt['data']http_response_code(200); // respond fast; process separately

How to test

  1. For a no-code test, point the webhook at a temporary catcher (e.g. a webhook.site bin) and ask us to enable it.
  2. Place a test call to your queue: you will see the signed POSTs arrive (call.ringing, answered, completed).
  3. Verify the signature with your secret before processing; respond 2xx immediately (on failure we retry with backoff).
You only receive events for your queue and agents. Use the event id (evt_…) to avoid processing a retry twice (idempotency).

📅 Booking calendar

Publish your free time slots and take bookings on your own website. Each booking creates the contact and the appointment in CRM → Calendar, with a video call link, confirmation email and reminder. Scopes calendar.slots, calendar.book and calendar.manage. Business hours, length and who attends are configured by TelHarbor.

Free slots GET /v1/calendar/slots

curl https://api.telharbor.com/v1/calendar/slots -H "Authorization: Bearer <API_KEY>"

Book POST /v1/calendar/book

curl -X POST https://api.telharbor.com/v1/calendar/book -H "Authorization: Bearer <API_KEY>" -H "Content-Type: application/json" \ -d '{"start":"2026-09-16T10:00:00-06:00","name":"Ana","email":"ana@empresa-demo.example","company":"Empresa","modality":"video","lang":"es","tz":"America/Costa_Rica","manage_url":"https://su-sitio.example/reserva/?t={token}"}'

Returns token (keep it: it grants access to the booking) and meet_url. If the slot was taken it returns 409 with slot_unavailable. To reschedule send replace_token with the previous token.

Look up and cancel GET /v1/calendar/booking?token=… POST /v1/calendar/cancel

⚠️ Errors

Error responses with ok:false and the matching HTTP status:

HTTPMeaning
401Missing or invalid API key.
403Missing required scope, IP not allowed, or resource not owned.
400Invalid parameters (e.g. from/to).
429Rate limit or daily quota exceeded.
502Provider / AMI failure while processing.

🎬 Video: a tour of your API

About two minutes: how to sign in securely, your keys and their limits, the usage log, requesting numbers, and how to connect using the documentation. It is narrated: turn the sound on.

Sample data with fictitious keys, addresses and numbers. The API portal page itself is in Spanish; the narration explains it in English. Below, the same screens explained point by point.

🧭 Your API in the portal

In the CPaaS → API's menu you check your keys, the log of every call your system makes, and request numbers. Keys are created and changed by the TelHarbor team. The events TelHarbor sends you are in Webhooks.

🔑 My API keys

The keys your system connects with, and what each one can do.

My API keys

Sample data: keys, IP addresses and numbers are fictitious; real keys are never shown in full. The API portal page itself exists only in Spanish — only the surrounding menu and header are translated. Tap the image to see it full size.

  1. 1
    Read-only

    Keys are viewed here; creating or changing them is done by the TelHarbor team.

  2. 2
    Name

    Which system the key is for.

  3. 3
    Prefix

    Only the start of the key, to recognize it. The full key is delivered once and never shown again.

  4. 4
    Scopes

    What it can do: send SMS or WhatsApp, check statuses, book, call…

  5. 5
    Status

    Active or disabled.

  6. 6
    Limits

    How many calls per minute and per day it allows.

  7. 7
    Last used

    When your system last used it.

  8. 8
    Disabled key

    It no longer works; if your system still uses it, it will get an error.

📊 Usage log

Every call your system made to the API, to check what happened.

Usage log

Sample data: keys, IP addresses and numbers are fictitious; real keys are never shown in full. The API portal page itself exists only in Spanish — only the surrounding menu and header are translated. Tap the image to see it full size.

  1. 1
    Calls today

    How many calls your system made today.

  2. 2
    Successful

    The ones that succeeded (2xx and 3xx).

  3. 3
    Errors

    The ones that failed (4xx and 5xx): check the row to see which.

  4. 4
    Endpoint

    Which service it called, for example /v1/sms/send.

  5. 5
    Method

    GET to query, POST to send.

  6. 6
    Scope

    The key scope that was used.

  7. 7
    HTTP code

    The response: 200 is success; 400 usually means bad data; 401 or 403, an invalid key or scope.

  8. 8
    IP

    Which address your system called from; useful if your key only accepts certain addresses.

  9. 9
    A row with an error

    An example of a rejected call, to find it by date and endpoint.

📱 Requesting a number

Request numbers to use with the API; the TelHarbor team reviews and activates them.

Requesting a number

Sample data: keys, IP addresses and numbers are fictitious; real keys are never shown in full. The API portal page itself exists only in Spanish — only the surrounding menu and header are translated. Tap the image to see it full size.

  1. 1
    Country

    The country and its code.

  2. 2
    Type

    For voice, SMS, WhatsApp or mixed.

  3. 3
    Quantity

    How many you need.

  4. 4
    Send request

    Sends it to the TelHarbor team; nothing is purchased automatically.

  5. 5
    Notes

    What you will use it for or which prefix you prefer.

  6. 6
    Status

    Pending, approved or already assigned.

  7. 7
    Team note

    What TelHarbor replied, for example the assigned number.

  8. 8
    Who manages it

    Keys and number provisioning are controlled and authorized by TelHarbor.