Vocatech

By area

Calls

Read from the API’s own reference file every hour.

Plain text

Your call history. Each call carries its journey through menus, groups and people, with AI summaries, transcripts and recording links.

List calls

GET/v1/calls

Your calls in a date window, newest first, one entry per call. Each call carries its journey: every leg it took through menus, groups, queues and people, with the AI summary, the transcript and a recording link when the leg was recorded.

Dates. Send start_date and end_date together, as local days in timezone. Send neither for today. A single date is ignored and you get today. At most 366 days per request; for more history, walk it a year at a time.

Paging. limit is 500 by default and at most 500. Calls that tie on the sort key keep a stable order, so paging is safe.

Filters. status, extension and type match single legs. A call is listed when one of its legs matches, and its journey then shows only the legs that match. search looks in the call id, the other party's name and number, your number and the extensions.

Fields to know. status is the status of the first leg in journey, not the call's final outcome: a call a menu answered and a person then missed reads answered. extension and extension_name name the last person who held the call. recording_url points at GET /v1/media/rec_<n>, which returns a signed link.

Recordings in parts. A call that is parked and picked up again is recorded in parts: part 1 up to the park, part 2 after the pick-up, rarely a part 3. Each leg lists its recordings in recordings, oldest first, each with its own url (GET /v1/media/rec_<n>?part=N) and its own start time and length. recording_url, summary and transcription are the latest part's. Nearly every leg has one part. The transcript of each part also comes by webhook (call.transcription, one event per part).

A search that runs longer than 30 seconds is stopped with 500. Narrow the window or add filters.

Each request is written to your account's HIPAA access log.

Request

curl -G https://api.vocatech.com/v1/calls \
  -H "Authorization: Bearer YOUR_API_KEY" \
  --data-urlencode "start_date=2026-09-21" \
  --data-urlencode "end_date=2026-09-22" \
  --data-urlencode "timezone=America/New_York"

Parameters

NameInTypeDescription
start_datequerystring (date)First local day of the window, YYYY-MM-DD, in timezone. Send it with end_date. Send neither for today. A single date is ignored and you get today. For example 2026-09-01.
end_datequerystring (date)Last local day of the window, YYYY-MM-DD, included in full. At most 366 days after start_date. For example 2026-09-07.
timezonequerystringIANA time zone for the dates, for example America/Chicago. An unknown zone falls back to America/New_York. The response echoes the zone used. Default America/New_York.
directionquerystringincoming or outgoing, as each call's direction reads. voicemail matches the rare calls stored that way. One of any, incoming, outgoing or voicemail. Default any.
statusquerystringOnly calls with a leg in this status, for example answered or missed. missed never returns an outgoing call. Default any. For example missed.
extensionquerystringOnly calls with a leg on this extension (exact match). For example 104.
typequerystringOnly calls with a leg of this type, for example user, auto_attendant, hunt_group or call_center. Default any.
searchquerystringText to find in the call id, the other party's name or number, your number, or an extension.
sortquerystringOrder by start time or by duration. Calls that tie keep a stable order, so paging is safe. One of day_desc, day_asc, duration_desc or duration_asc. Default day_desc.
pagequeryintegerPage number, from 1. Default 1. At least 1.
limitqueryintegerRows per page, 1 to 500. A value outside that range is brought inside it. Default 500. From 1 to 500.
company_idqueryintegerOptional. If you send it, it must be your own company id, or the request is refused with 403. The key already decides the company.

Answers

200
The calls in the window.
FieldTypeDescription
query requiredobjectThe filters that were applied, echoed back. extension and type are applied but not echoed.
query.company_idinteger
query.start_datestring (date)The first local day used.
query.end_datestring (date)The last local day used.
query.directionstring
query.statusstring
query.searchstring
query.timezonestringThe time zone used.
calls requiredarray of objects
calls[].call_id requiredstringThe call's id.
calls[].direction requiredstringincoming or outgoing. A call your staff placed through the dial-out portal reads outgoing. A few calls read voicemail.
calls[].status requiredstringThe status of the first leg in journey, for example answered or missed. It is not the call's final outcome: a call a menu answered and a person then missed reads answered. Read the legs to see who answered.
calls[].extension requiredstring or nullThe last person who held the call. When no person did, the first extension on it.
calls[].extension_name requiredstring or nullThat extension's name, written First Last.
calls[].remote_name requiredstringThe other party's name from caller ID, written First Last. Can be empty.
calls[].remote_number requiredstringThe other party's number, digits without the leading 1. Can be empty.
calls[].group_number requiredstringYour number on the call, digits without the leading 1. Can be empty.
calls[].start_time requiredstring (date-time) or nullUTC, ISO 8601 with milliseconds.
calls[].end_time requiredstring (date-time) or nullUTC, ISO 8601 with milliseconds.
calls[].duration requiredintegerThe call's length in seconds.
calls[].journey requiredarray of objectsEvery leg, in order. When you filter by status, extension or type, only the matching legs are listed.
calls[].journey[].order requiredintegerPosition in the journey, from 1.
calls[].journey[].type requiredstringWhat handled this leg, as the phone system wrote it. For example user (a person), auto_attendant, hunt_group or call_center.
calls[].journey[].extension_name requiredstring or nullThe name of the person or feature on this leg, as set in the portal, written First Last.
calls[].journey[].extension requiredstring or nullThe extension on this leg.
calls[].journey[].start_time requiredstring (date-time) or nullUTC, ISO 8601 with milliseconds.
calls[].journey[].end_time requiredstring (date-time) or nullUTC, ISO 8601 with milliseconds.
calls[].journey[].duration requiredintegerSeconds on this leg.
calls[].journey[].summary requiredstring or nullThe AI summary of this leg's recording (its latest part). Null when there is none, and when the leg's only recording belongs to another call leg.
calls[].journey[].transcription requiredstring or nullThe transcript of this leg's recording (its latest part), with speaker labels. Null when there is none, and when the leg's only recording belongs to another call leg.
calls[].journey[].recording_url requiredstring (uri) or nullWhere to fetch this leg's recording (its latest part). Call it with your key (GET /v1/media/rec_<n>) to get a signed link. Null when the leg was not recorded.
calls[].journey[].recordings requiredarray of objectsEvery recording of this leg, oldest first. A call parked and picked up again has two parts (rarely three). Nearly every recorded leg has one. Empty when the leg was not recorded.
calls[].journey[].recordings[].part requiredintegerThis part's place, from 1, oldest first.
calls[].journey[].recordings[].parts requiredintegerHow many parts the leg's recording has.
calls[].journey[].recordings[].recording_id requiredintegerThe recording's id. The call.transcription event of this part carries the same id.
calls[].journey[].recordings[].start_time requiredstring (date-time) or nullWhen this part started. UTC, ISO 8601 with milliseconds.
calls[].journey[].recordings[].duration requiredinteger or nullSeconds.
calls[].journey[].recordings[].url requiredstring (uri) or nullWhere to fetch this part (GET /v1/media/rec_<n>?part=N) with your key. Null while the part's file is not stored yet.
meta requiredobjectPaging for list routes. Each list adds its own total.
meta.page requiredintegerThis page, from 1.
meta.limit requiredintegerRows per page, after limits were applied.
meta.total_pages requiredintegerHow many pages there are.
meta.total_calls requiredintegerCalls that match, across all pages.
Example
{
  "query": {
    "company_id": 1234567890,
    "start_date": "2026-09-21",
    "end_date": "2026-09-22",
    "direction": "any",
    "status": "any",
    "search": "",
    "timezone": "America/New_York"
  },
  "calls": [
    {
      "call_id": "3c9d7e1f-2a4b-4c6d-8e0f-1a2b3c4d5e6f",
      "direction": "outgoing",
      "status": "answered",
      "extension": "104",
      "extension_name": "Maya Chen",
      "remote_name": "",
      "remote_number": "7185550142",
      "group_number": "7185550100",
      "start_time": "2026-09-22T15:40:02.000Z",
      "end_time": "2026-09-22T15:41:10.000Z",
      "duration": 68,
      "journey": [
        {
          "order": 1,
          "type": "user",
          "extension_name": "Maya Chen",
          "extension": "104",
          "start_time": "2026-09-22T15:40:02.000Z",
          "end_time": "2026-09-22T15:41:10.000Z",
          "duration": 68,
          "summary": null,
          "transcription": null,
          "recording_url": null,
          "recordings": []
        }
      ]
    },
    {
      "call_id": "8a1f3c2e-5b7d-4e9a-9c1b-2d4e6f8a0b1c",
      "direction": "incoming",
      "status": "answered",
      "extension": "104",
      "extension_name": "Maya Chen",
      "remote_name": "Jordan Reyes",
      "remote_number": "7185550142",
      "group_number": "7185550100",
      "start_time": "2026-09-21T14:03:12.000Z",
      "end_time": "2026-09-21T14:09:40.000Z",
      "duration": 388,
      "journey": [
        {
          "order": 1,
          "type": "auto_attendant",
          "extension_name": "Main Menu",
          "extension": "100",
          "start_time": "2026-09-21T14:03:12.000Z",
          "end_time": "2026-09-21T14:03:31.000Z",
          "duration": 19,
          "summary": null,
          "transcription": null,
          "recording_url": null,
          "recordings": []
        },
        {
          "order": 2,
          "type": "user",
          "extension_name": "Maya Chen",
          "extension": "104",
          "start_time": "2026-09-21T14:03:31.000Z",
          "end_time": "2026-09-21T14:09:40.000Z",
          "duration": 369,
          "summary": "Jordan Reyes asked to move the Thursday appointment to Friday at 10 AM. Maya Chen confirmed the new time and said a reminder text will follow.",
          "transcription": "Speaker 1: Good morning, thanks for calling. This is Maya.\nSpeaker 2: Hi Maya, this is Jordan Reyes. I need to move my Thursday appointment.\nSpeaker 1: No problem. Would Friday at 10 AM work?\nSpeaker 2: Friday at 10 is perfect. Thank you.",
          "recording_url": "https://api.vocatech.com/v1/media/rec_48213907",
          "recordings": [
            {
              "part": 1,
              "parts": 2,
              "recording_id": 10577640,
              "start_time": "2026-09-21T14:03:32.000Z",
              "duration": 41,
              "url": "https://api.vocatech.com/v1/media/rec_48213907?part=1"
            },
            {
              "part": 2,
              "parts": 2,
              "recording_id": 10577702,
              "start_time": "2026-09-21T14:04:31.000Z",
              "duration": 309,
              "url": "https://api.vocatech.com/v1/media/rec_48213907?part=2"
            }
          ]
        }
      ]
    }
  ],
  "meta": {
    "page": 1,
    "limit": 500,
    "total_pages": 1,
    "total_calls": 2
  }
}
400
A date that is not YYYY-MM-DD, an end before the start, or a window over 366 days.
Example bodies
Example
{
  "error": {
    "code": 400,
    "type": "BAD_REQUEST",
    "message": "Invalid date format or timezone. Use YYYY-MM-DD and a valid IANA timezone."
  }
}
Example
{
  "error": {
    "code": 400,
    "type": "BAD_REQUEST",
    "message": "end_date is before start_date."
  }
}
Example
{
  "error": {
    "code": 400,
    "type": "BAD_REQUEST",
    "message": "Ask for at most 366 days at a time (this request covers 400). For more history, walk it a year at a time."
  }
}
401
No key, or a key that is wrong or turned off.
403
Refused. The key is scoped away from this route, the request came from an address not on the key's allow-list, or the route refused the request (the message says why).
429
A rate limit was reached. Wait Retry-After seconds. The body names the counter, the window and the limit.
HeaderTypeDescription
Retry-AfterintegerSeconds to wait before you try again.
500
An unexpected fault on our side, or a search that ran longer than 30 seconds and was stopped.
503
Try again shortly. The key could not be checked just now (Retry-After: 5), or a service this route needs could not be reached.
HeaderTypeDescription
Retry-AfterintegerSeconds to wait before you try again.