Developer Guide
Everything you need to integrate with the Vocatech API for call reporting, messaging, contact management, and real-time webhooks.
Overview
The Vocatech API is a REST API that gives you access to:
- Call history — Search and filter call records with full journey details.
- Message history — Search SMS and WhatsApp message records.
- Messaging — Send SMS messages through your Vocatech numbers.
- Contacts — Manage integration contacts: pull lists, push updates, and sync from external systems.
- Webhooks — Subscribe to real-time call and message events delivered to your endpoint.
Base URL
https://api.vocatech.com/v1
All API endpoints are relative to this base URL. For example, to list calls:
GET https://api.vocatech.com/v1/calls
Authentication
All requests require a Bearer token in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Your API key is scoped to a single company. All queries are automatically filtered to your company's data.
Example Request
curl -X GET "https://api.vocatech.com/v1/calls" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"
Calls
| Method | Endpoint | Description |
|---|---|---|
| GET | /calls |
Search call history with filters |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
start_date | date | No | Start date (YYYY-MM-DD). Defaults to today. |
end_date | date | No | End date (YYYY-MM-DD). Defaults to today. |
direction | string | No | incoming, outgoing, or any (default) |
status | string | No | answered, missed, or any (default) |
search | string | No | Search by call ID, name, or extension |
page | integer | No | Page number |
limit | integer | No | Results per page (max 500) |
Example
curl "https://api.vocatech.com/v1/calls?start_date=2026-03-01&end_date=2026-03-10&direction=incoming" \
-H "Authorization: Bearer YOUR_API_KEY"
Call Journey
Each call record includes a journey array showing every segment of the call in chronological order:
| Field | Type | Description |
|---|---|---|
type | string | AutoAttendant, HuntGroup, CallCenter, or User |
name | string | Name of the entity (e.g., "Main Menu", "Sales Queue", "John Doe") |
extension | string | Extension number |
start_time | datetime | When this segment started |
duration | integer | Segment duration in seconds |
answered | boolean | Whether the call was answered in this segment |
Messages
| Method | Endpoint | Description |
|---|---|---|
| POST | /messages |
Send an SMS message |
| GET | /messages |
Search message history |
Send a Message
curl -X POST "https://api.vocatech.com/v1/messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platform": "text",
"from": "5551234567",
"to": "5559876543",
"message": "Hello from Vocatech!"
}'
"test": true to the request body to validate without actually sending. Returns a 200 with validation results instead of creating any resources.
Message History Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
start_date | date | No | Start date (YYYY-MM-DD) |
end_date | date | No | End date (YYYY-MM-DD) |
direction | string | No | incoming, outgoing, or any |
channel | string | No | text, whatsapp, or any |
status | string | No | delivered, failed, read, or any |
Media
The unified GET /v1/media/{id} endpoint provides authenticated access to all media files — SMS/MMS attachments, WhatsApp media, and call recordings. The ID prefix determines the media type.
| Method | Endpoint | Description |
|---|---|---|
| GET | /media/{id} |
Get media metadata and download URL |
ID Prefixes
| Prefix | Type | Example | Source |
|---|---|---|---|
att_ | Message attachment | /v1/media/att_456 | SMS/MMS images, WhatsApp photos, documents, voice notes |
rec_ | Call recording | /v1/media/rec_84729 | Call recording audio from call journey segments |
Message Attachments (att_)
When a message includes media, the attachments array in the message response or webhook payload contains attachment objects:
{
"message_id": "sms_12345",
"direction": "incoming",
"channel": "text",
"body": "Check out this photo!",
"attachments": [
{
"id": "att_456",
"content_type": "image/jpeg",
"filename": "photo1.jpg",
"size": 245760
}
]
}
Download the attachment:
curl "https://api.vocatech.com/v1/media/att_456" \
-H "Authorization: Bearer YOUR_API_KEY"
Supported Media Types
| Channel | Supported Types |
|---|---|
| SMS/MMS | Images (JPEG, PNG, GIF), contacts, and other MMS-supported formats |
| Images, video (MP4), audio (MP3, OGG, AMR), documents (PDF, DOCX, XLSX), contacts (VCF) |
attachments field is always present. For text-only messages it will be [].
Call Recordings (rec_)
Call recordings are accessed through the same /v1/media endpoint using the rec_ prefix with the segment ID from the call journey.
How It Works
- Call
GET /v1/callsto retrieve call reports — each journey segment includes arecording_urlfield (null if no recording exists). - The
recording_urlpoints to/v1/media/rec_{segment_id}— call it to get a 30-minute signed download URL. - Download the MP3 file directly using the signed URL (no auth header needed for the download itself).
curl "https://api.vocatech.com/v1/media/rec_84729" \
-H "Authorization: Bearer YOUR_API_KEY"
Recording Response
{
"id": "rec_84729",
"type": "recording",
"content_type": "audio/mp3",
"format": "mp3",
"duration": 185,
"url": "https://storage.googleapis.com/...signed...",
"expires_at": "2026-03-20T19:30:00.000Z"
}
Notes
- Signed URLs expire after 30 minutes. Request a new URL by calling the endpoint again.
- Only media belonging to your company is accessible — ownership is verified automatically.
- Not all call segments have recordings. Check the
recording_urlfield in the call journey — if null, no recording is available.
Contacts
Manage your integration contacts programmatically. Pull existing contact lists, push new contacts, or sync from external systems like CRMs and property management software.
| Method | Endpoint | Description |
|---|---|---|
| GET | /contacts/fields |
List your contact field schema |
| GET | /contacts |
List contacts (paginated, searchable) |
| POST | /contacts |
Create or update contacts (single or batch) |
| DELETE | /contacts |
Delete contacts (single or batch) |
Step 1: Get Your Field Schema
Before pushing contacts, call GET /contacts/fields to see what fields are configured for your company:
curl "https://api.vocatech.com/v1/contacts/fields" \
-H "Authorization: Bearer YOUR_API_KEY"
Response:
{
"fields": [
{ "id": 12, "name": "Company", "is_phone": false, "is_match": true, "is_integration": false },
{ "id": 18, "name": "Phone", "is_phone": true, "is_match": false, "is_integration": false },
{
"id": 20, "name": "Portal", "is_phone": false, "is_match": false,
"is_integration": true,
"integration": {
"id": 1, "initials": "AP",
"fields": [
{ "key": "contact_url", "display_name": "Contact URL", "value": "https://admin.vocatech.com/company/?company_id={CONTACT_ID}" },
{ "key": "default_url", "display_name": "Default URL", "value": "https://admin.vocatech.com" }
]
}
}
]
}
- Match fields (
is_match: true) are used for deduplication. When you push a contact, if ALL match fields match an existing contact, it updates instead of creating a duplicate. - Phone fields (
is_phone: true) auto-format values to digits. They support multiple numbers separated by semicolons (e.g.,"5551234567;5559876543"). - Integration fields (
is_integration: true) are clickable connectors with initials and URLs. The field value acts as a reference ID for generating links.
Step 2: List Existing Contacts
curl "https://api.vocatech.com/v1/contacts?page=1&limit=100" \
-H "Authorization: Bearer YOUR_API_KEY"
Response:
{
"query": { "page": 1, "limit": 100, "search": null },
"contacts": [
{
"id": 181119,
"fields": {
"Company": "ABC Mortgage",
"Contact": "John Smith",
"Phone": "2125551234;2125559999;2125550100",
"Portal": "9105222634"
}
}
],
"meta": {
"page": 1,
"limit": 100,
"total_pages": 19,
"total_contacts": 1858
}
}
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number (default: 1) |
limit | integer | No | Results per page, max 500 (default: 100) |
search | string | No | Search across all field values |
Step 3: Push Contacts
Send a single contact:
curl -X POST "https://api.vocatech.com/v1/contacts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fields": {
"Company": "ABC Mortgage",
"Contact": "John Smith",
"Phone": "2125551234;2125559999;2125550100",
"Portal": "9105222634"
}
}'
Response (201 if created, 200 if updated):
{
"summary": { "total": 1, "created": 1, "updated": 0, "errors": 0 },
"contacts": [
{
"contact": {
"id": 181119,
"fields": {
"Company": "ABC Mortgage",
"Contact": "John Smith",
"Phone": "2125551234;2125559999;2125550100",
"Portal": "9105222634"
}
},
"action": "created"
}
],
"errors": []
}
"5551234567;5559876543;5554441111". Each number is formatted individually and duplicates are removed.
Batch Push
Send up to 500 contacts at once by using the contacts array:
curl -X POST "https://api.vocatech.com/v1/contacts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contacts": [
{ "fields": { "Company": "ABC Mortgage", "Phone": "2125551234;2125559999", "Portal": "9105222634" } },
{ "fields": { "Company": "XYZ Corp", "Phone": "7185558888", "Portal": "9105333745" } }
]
}'
Response:
{
"summary": { "total": 2, "created": 1, "updated": 1, "errors": 0 },
"contacts": [ ... ],
"errors": []
}
Delete Contacts
Delete a single contact by ID:
curl -X DELETE "https://api.vocatech.com/v1/contacts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "id": 12345 }'
Delete multiple contacts:
curl -X DELETE "https://api.vocatech.com/v1/contacts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "ids": [12345, 12346, 12347] }'
Webhooks
Webhooks let you receive real-time notifications when call or message events occur. Create an endpoint, subscribe to events, and we'll send HTTP POST requests to your URL.
| Method | Endpoint | Description |
|---|---|---|
| GET | /webhooks |
List all webhook endpoints |
| POST | /webhooks |
Create a webhook endpoint |
| GET | /webhooks/{id} |
View a webhook endpoint |
| PATCH | /webhooks/{id} |
Update a webhook endpoint |
| DELETE | /webhooks/{id} |
Delete a webhook endpoint |
| POST | /webhooks/{id}/test |
Send a test delivery |
| GET | /webhooks/failures |
List failed deliveries |
Create a Webhook
curl -X POST "https://api.vocatech.com/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "My CRM Webhook",
"url": "https://yourapp.com/webhook",
"event_filters": ["call.started", "call.ended", "message.received", "message.sent"]
}'
secret_key is only returned when you create the webhook. Store it securely — you'll need it to verify delivery signatures.
Event Types
| Event | When it fires |
|---|---|
call.started | A call leg begins (dialled or received) |
call.answered | A call leg is answered |
call.ended | A call leg finishes |
call.transcription | AI processing completes (5-30 min after call ends) |
| Message Events | |
message.sent | An outgoing SMS or WhatsApp message is sent |
message.received | An inbound SMS or WhatsApp message arrives |
message.status_updated | Delivery status changes (sent → delivered → read) |
Subscribe to one or more events via the event_filters array. You can mix call and message events in a single webhook.
You can also filter by extension using extension_filters to only receive call events for specific extensions.
Webhook Payloads
When an event fires, your endpoint receives a JSON POST with this structure:
{
"id": "evt_67890abcdef",
"event_type": "call.ended",
"timestamp": "2026-03-10T14:30:45+00:00",
"company_id": 123,
"data": {
"call_id": "c045ac31-abbe-45dc-9722-efbe4044cc73",
"direction": "incoming",
"extension": "101",
"extension_name": "John Doe",
"remote_name": "Jane Smith",
"remote_number": "5551234567",
"group_number": "5559876543",
"start_time": "2026-03-10T14:30:00.000Z",
"end_time": "2026-03-10T14:31:10.000Z",
"duration": 70
}
}
For call.started and call.answered events, the end_time and duration fields are not included.
Message Event Payload
Message events use the same envelope with a different data shape. The attachments array is always present (empty for text-only messages):
{
"id": "evt_msg_abc12345",
"event_type": "message.received",
"timestamp": "2026-03-15T14:30:45+00:00",
"company_id": 123,
"data": {
"message_id": "sms_789012",
"direction": "incoming",
"channel": "text",
"from": "+14155551234",
"to": "+17185550000",
"body": "Check out this photo!",
"status": "delivered",
"sent_at": "2026-03-15T14:30:45.000Z",
"attachments": [
{
"url": "https://api.vocatech.com/v1/media/att_456",
"content_type": "image/jpeg",
"filename": "photo1.jpg",
"size": 245760
}
]
}
}
| Field | Type | Description |
|---|---|---|
message_id | string | Unique message identifier |
direction | string | incoming or outgoing |
channel | string | text (SMS) or whatsapp |
from | string | Sender phone number (E.164) |
to | string | Recipient phone number (E.164) |
body | string | Message text content |
status | string | sent, delivered, read, or failed |
sent_at | datetime | When the message was sent (UTC) |
attachments | array | Media attachments (empty array [] for text-only messages). Each object has url, content_type, filename, and size. |
For message.status_updated events, the data object includes the new status value and an updated_at timestamp. The read status is only available for WhatsApp messages.
For call.transcription events, the data object also includes:
| Field | Type | Description |
|---|---|---|
summary | string | AI-generated call summary |
transcription | string | Full transcript text |
Signature Verification
Every delivery includes an X-Vocatech-Signature header for security:
X-Vocatech-Signature: t=1710000000,v1=5257a8...b4c2e9
How to Verify
1. Extract the timestamp (t) and signature (v1) from the header.
2. Compute the expected signature:
// PHP
$expected = hash_hmac('sha256', "t={$timestamp}.{$rawBody}", $secretKey);
// Node.js
const expected = crypto
.createHmac('sha256', secretKey)
.update(`t=${timestamp}.${rawBody}`)
.digest('hex');
// Python
import hmac, hashlib
expected = hmac.new(secret_key.encode(), f"t={timestamp}.{raw_body}".encode(), hashlib.sha256).hexdigest()
3. Compare: if v1 matches your computed value, the delivery is authentic.
4. Replay protection: reject if abs(current_time - t) > 300 (5 minutes).
Retry Policy
If your endpoint returns a non-2xx status code or times out, we retry with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
After 5 total attempts, the delivery is marked as failed. View failures at GET /webhooks/failures.
Webhook Testing
Two ways to test your webhooks:
1. Test Tool (no server needed)
Use our interactive test tool at api.vocatech.com/test. It creates a temporary receiver URL so you can see exactly what payloads look like.
2. Send a Test Delivery
Send a test event to your actual endpoint:
curl -X POST "https://api.vocatech.com/v1/webhooks/{id}/test" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
This sends a webhook.test event to the endpoint's URL with a valid signature.
Rate Limits
Rate limits are applied per API key:
| Tier | Endpoints | Limits |
|---|---|---|
| Default | Most endpoints | 60/min • 1,000/hr • 20,000/day |
| Reports | GET /calls, GET /messages |
30/min • 500/hr • 10,000/day |
| Messaging | POST /messages |
5 per 10 minutes |
When exceeded, the API returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait.
Errors
The API uses standard HTTP status codes. Error responses include a JSON body:
{
"error": {
"code": 404,
"type": "RESOURCE_NOT_FOUND",
"message": "Webhook endpoint not found."
}
}
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 204 | Deleted (no content) |
| 400 | Bad request (missing or invalid fields) |
| 401 | Unauthorized (invalid or missing API key) |
| 403 | Forbidden (no access to this resource) |
| 404 | Not found |
| 409 | Conflict (deprecated — existing resources are now reused) |
| 422 | Validation error |
| 429 | Rate limit exceeded |
Pagination
List endpoints support pagination with page and limit query parameters:
GET /v1/calls?page=2&limit=50
Responses include a meta object:
{
"meta": {
"page": 2,
"limit": 50,
"total_pages": 10,
"total_calls": 487
}
}
Need help? Contact support@vocatech.com