Integration Use Case
Pagination, Search, Sorting and Filtering in the Hipcall API
Pagination with limit/offset, q search, sort ordering and bracket filters in the Hipcall API, with measured notes on boundary values and error responses.
Part A: Pagination
A1. Calling with no parameters
Command:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
When the request is sent with no pagination parameters, it returns 10 records by default. The top-level structure of the response is a JSON object containing a data and a meta field.
Full response body (phone numbers masked):
{
"data": [
{
"caller_type": "user",
"missing_call_reason": null,
"first_touch_duration": 3,
"ended_at": "2026-09-15T11:18:09Z",
"uuid": "ff111433-dd1d-4d61-abd3-4b06fa9cd43f",
"started_at": "2026-09-15T11:17:56Z",
"user_id": 4200,
"number_id": 938,
"caller_id": 4200,
"direction": "outbound",
"voicemail_id": null,
"answered_at": null,
"company_id": null,
"missing_call": false,
"call_duration": 0,
"callee_type": "contact",
"callback_user_id": null,
"callee_id": null,
"voicemail_url": null,
"callback_cdr_uuid": null,
"bridged_at": "2026-09-15T11:17:59Z",
"team_touch_at": null,
"call_flow": [
{
"action": "hangup",
"detail": {
"hangup_by": "user"
},
"timestamp": 1789471089
},
{
"action": "bridge",
"detail": {
"id": 4200,
"type": "user"
},
"timestamp": 1789471079
},
{
"action": "init",
"detail": {
"id": null,
"type": "contact"
},
"timestamp": 1789471076
}
],
"callback_time": null,
"callee_number": "+90XXXXXXXXXX",
"voicemail_type": null,
"channel_id": 938,
"hangup_by": "user",
"credited": false,
"caller_number": "+90XXXXXXXXXX",
"record_url": null,
"channel_type": "number",
"contact_id": null
},
],
"meta": {
"count": 34,
"offset": 0,
"limit": 10
}
}
Note: the returned body (the data array) is very long, so only the first call record is shown here for readability, and phone numbers are masked for security.
A2. What is there besides data?
Besides data, the response contains a meta object.
Fields in the meta object:
| Field | Description |
|---|---|
count | Total number of records matching the query and the applied filters. |
offset | Where the listing starts, i.e. how many records were skipped. |
limit | Number of records returned in a single request. |
In this call’s response, count was 34, offset was 0 and limit was 10.
A3. Upper bound of the limit parameter
The upper bound of limit was found to be 100. When limit=101 is sent, the API rejects the request.
Test command:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?limit=101" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
{
"errors": {
"limit": [
"101 is larger than inclusive maximum 100"
]
}
}
Rather than clamping the excess value down to 100, the API rejects the request with an error.
A4. limit=0 and limit=-5
limit=0 test:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?limit=0" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
{
"errors": {
"limit": [
"0 is smaller than inclusive minimum 1"
]
}
}
limit=-5 test:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?limit=-5" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
{
"errors": {
"limit": [
"-5 is smaller than inclusive minimum 1"
]
}
}
In both cases the API rejects the request. Based on these tests, limit cannot be smaller than 1.
A5. Fetching the second page with offset
Since limit defaults to 10 on the first request, offset=10 must be used to get the second page. offset=2 does not mean “second page”; it only skips the first two records and starts the listing at the third.
Command for the second page:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?offset=10" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
A6. Total record count and page arithmetic
The total record count is read from the count field of the meta object.
In this test:
meta.count = 34
meta.limit = 10
The total number of pages is found by dividing the total record count by the page size and rounding up.
Total Pages = ceil(meta.count / meta.limit)
Total Pages = ceil(34 / 10)
Total Pages = 4
Formula:
Total Pages = ceil(meta.count / meta.limit)
On the C# side:
Math.Ceiling((double)meta.count / meta.limit)
So for 34 records with 10 records per page, a total of 4 pages must be fetched.
Part B: Search and sorting
B1. Searching with the q parameter
On /api/v3/contacts, search works both by name and by a fragment of a phone number.
Name search (q=Bill):
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/contacts?q=Bill" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
{
"data": [
{
"id": 190987,
"user": {
"id": 4200,
"email": "ornek@firma.com",
"full_name": "Ahmet Y.",
"first_name": "Ahmet",
"last_name": "Y."
},
"source": null,
"external_id": null,
"full_name": "Bill Gates",
"first_name": "Bill",
"last_name": "Gates",
"company": {
"id": 80684,
"name": "Ornek Firma",
"linkedin_url": "https://www.linkedin.com/search/...",
"website_url": "https://ornek.firma.com/"
},
"custom_url": "https://custom.com",
"linkedin_url": "https://linkedin.com/in/williamhgates",
"emails": [
{
"order": 1,
"email": "ornek@firma.com"
}
],
"life_cycle": null,
"phones": [
{
"id": 246084,
"number": "+1XXXXXXXXXX",
"order": 1,
"country": "US"
}
],
"sector": null,
"job_title": "Co-founder",
"custom_fields": {}
}
],
"meta": {
"count": 1,
"offset": 0,
"limit": 10
}
}
Phone number fragment search (q=2964):
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/contacts?q=2964" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output summary:
{
"data": [
{
"id": 190953,
"user": {
"id": 4200,
"email": "ornek@firma.com",
"full_name": "Ahmet Y.",
"first_name": "Ahmet",
"last_name": "Y."
},
"source": null,
"external_id": null,
"full_name": "Ahmet Y.",
"first_name": "Ahmet",
"last_name": "Y.",
"company": {
"id": 80684,
"name": "Ornek Firma",
"linkedin_url": "https://www.linkedin.com/search/...",
"website_url": "https://ornek.firma.com/"
},
"custom_url": "https://www.linkedin.com/search/...",
"linkedin_url": "https://www.linkedin.com/search/...",
"emails": [
{
"order": 1,
"email": "ornek@firma.com"
}
],
"life_cycle": null,
"phones": [
{
"id": 246052,
"number": "+90XXXXXXXXXX",
"order": 1,
"country": "TR"
}
],
"sector": null,
"job_title": "fabsava",
"custom_fields": {}
}
],
"meta": {
"count": 8,
"offset": 0,
"limit": 10
}
}
The phone number fragment search returned 8 matching records.
B2. Behaviour of q when nothing matches
When a search matches no records, the API does not return 404 Not Found. It returns a successful response instead, with an empty data array and meta.count set to 0.
Test command:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/contacts?q=xyz123abc" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
{
"data": [],
"meta": {
"count": 0,
"offset": 0,
"limit": 10
}
}
B3. The sort parameter
The sort parameter is used by placing a dot (.) between the field to sort on and the sort direction.
Example:
sort=id.asc
Use asc for ascending and desc for descending order.
If more than one sort field is needed, the fields are separated with a comma (,). Variants that control the position of null values, such as asc_nulls_last and desc_nulls_first, are also supported.
B4. Sort fields per endpoint
Sort fields vary by endpoint.
| Endpoint | Supported sort fields |
|---|---|
/api/v3/calls | started_at |
/api/v3/contacts | id, first_name, last_name, full_name |
For example, trying to sort /api/v3/calls by the unsupported user_id field makes the API reject the request:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?offset=2&sort=user_id.asc" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
{
"errors": {
"sort": [
"Invalid sort fields: [\"user_id.asc\"]"
]
}
}
Using an unsupported sort field on /api/v3/contacts produces the same 422 Unprocessable Entity error and Invalid sort fields message.
B5. An unsupported sort field
When an unsupported sort field is sent, the API rejects the request and returns 422 Unprocessable Entity.
Example:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/contacts?sort=telefon_numarasi.asc" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Response:
{
"errors": {
"sort": [
"Invalid sort fields: [\"telefon_numarasi.asc\"]"
]
}
}
B6. Default sorting
When no sort parameter is sent, endpoints do not all use the same default ordering.
/api/v3/contacts results are returned in id.asc order by default.
/api/v3/calls results are returned in descending order of started_at by default, i.e. newest calls first.
Part C: Filtering
C1. Date range filter
On the /api/v3/calls endpoint, dates are filtered with the gte (greater than or equal) and lte (less than or equal) operators on the started_at field.
The eq operator is not supported on started_at and returns an Unexpected field: eq error when used.
Date range test:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?started_at%5Bgte%5D=2026-09-17T00%3A00%3A00Z&started_at%5Blte%5D=2026-09-17T16%3A00%3A00Z" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
{
"data": [
{
"caller_type": "user",
"missing_call_reason": null,
"first_touch_duration": 9,
"ended_at": "2026-09-17T10:43:40Z",
"uuid": "54cb143f-11d9-4a81-9eaf-5ada7b953e19",
"started_at": "2026-09-17T10:43:30Z",
"user_id": 4200,
"number_id": 939,
"caller_id": 4200,
"direction": "outbound",
"voicemail_id": null,
"answered_at": "2026-09-17T10:43:39Z",
"company_id": 80681,
"missing_call": false,
"call_duration": 10,
"callee_type": "contact",
"callback_user_id": null,
"callee_id": null,
"voicemail_url": null,
"callback_cdr_uuid": null,
"bridged_at": "2026-09-17T10:43:39Z",
"team_touch_at": null,
"call_flow": [
{
"action": "hangup",
"detail": {
"hangup_by": "contact"
},
"timestamp": 1789641820
},
{
"action": "bridge",
"detail": {
"id": 4200,
"type": "user"
},
"timestamp": 1789641811
},
{
"action": "init",
"detail": {
"id": null,
"type": "contact"
},
"timestamp": 1789641810
}
],
"callback_time": null,
"callee_number": "+90XXXXXXXXXX",
"voicemail_type": null,
"channel_id": 939,
"hangup_by": "contact",
"credited": false,
"caller_number": "+90XXXXXXXXXX",
"record_url": null,
"channel_type": "number",
"contact_id": 190988
}
],
"meta": {
"count": 1,
"offset": 0,
"limit": 10
}
}
A single call record was returned within this range, with count: 1.
C2. Filtering by call direction
The call direction filter takes text rather than a number. Supported values include inbound and outbound.
Test with a text value:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?direction%5Beq%5D=inbound" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
The request sent with inbound returns results successfully.
Test with a numeric value:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?direction%5Beq%5D=1" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
{
"errors": {
"direction": [
"Value must be a string."
]
}
}
So the value of the direction filter must be sent as text.
C3. Filtering on multiple values
Multiple values can be filtered with the in operator. Values are separated with a comma (,).
Test command:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?direction%5Bin%5D=inbound,outbound" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
The result list is filtered as expected.
C4. Filtering missed calls
Missed calls are filtered with the missing_call field. This field accepts the eq operator.
Example usage:
missing_call[eq]=true
Test command:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?missing_call%5Beq%5D=true" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
No separate record output was observed in this test.
C5. Using several filters at once
Date and direction filters can be used together in the same request. Filters are joined in the URL with &.
Test command:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?started_at%5Bgte%5D=2026-09-17T00%3A00%3A00Z&direction%5Beq%5D=outbound" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
This request works and returns the filtered call records. The test responded with 200 OK.
C6. Date filter on /api/v3/contacts
The /api/v3/contacts endpoint does not support a date filter on the created_at field.
Rather than being silently ignored, the filter makes the API reject the request.
Test command:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/contacts?created_at%5Bgte%5D=2026-01-01T00%3A00%3A00Z" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
{
"errors": {
"created_at": [
"Unexpected field: created_at"
]
}
}
So /api/v3/contacts does not ignore the unsupported created_at filter; it returns an error instead.
C7. A filter operator that does not exist
When an unsupported operator is used, the API rejects the request.
Test command:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?started_at%5Byakin%5D=2026-09-01T00%3A00%3A00Z" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
{
"errors": {
"started_at": [
"#/started_at/yakin: Unexpected field: yakin"
]
}
}
So the unsupported yakin operator is not accepted and the API returns an error.
C8. A malformed date value
The date value in the started_at filter must be in ISO 8601 format.
Test command:
curl -X "GET" \
"https://use.hipcall.com.tr/api/v3/calls?started_at%5Bgte%5D=dün" \
-H "accept: application/json" \
-H "Authorization: Bearer $HIPCALL_API_TOKEN"
Output:
{
"errors": {
"started_at": [
"Invalid datetime format (expected ISO8601)"
]
}
}
The API does not accept the invalid date format and states plainly that it expects an ISO 8601 value.
Findings
Comparing C6, C7 and C8
Placing the API’s reactions to bad filter parameters side by side shows that the system has a strict and safe validation mechanism:
- Malformed value (C8): when data outside the expected format (ISO 8601) is sent, the API errors immediately.
- Invalid operator (C7): when an unsupported operator (
[yakin]) is used, the API rejects the operation. - Unsupported filter field (C6): when you try to filter on a field an endpoint has no equivalent for (
created_at), the API does not silently ignore the filter and return everything; it throws anUnexpected fielderror and stops the flow.
Opinion: this strict behaviour is very good news for a developer. If the API silently ignored a misspelled or unsupported filter, a developer could assume their filter worked and go on to operate on missing or wrong data (e.g. pulling the entire database while believing “I fetched the last month”). Erroring on every wrong step prevents faulty reports and data inconsistencies that would only surface months later.
Endpoint comparison table
| Endpoint | q (search) | sort fields | Bracket filters |
|---|---|---|---|
/api/v3/calls | ❌ Not supported | started_at | started_at[gte/lte], direction[eq/in], missing_call[eq] |
/api/v3/contacts | ✅ Name, phone | id, first_name, last_name, full_name | ❌ No date filters (created_at not supported) |
/api/v3/companies | ✅ Supported | id, name | ❌ Not supported |
/api/v3/tasks | ✅ Supported | id | ❌ Not supported |
Pagination flow
flowchart TD
A["Start: offset = 0"] --> B["GET /api/v3/calls\n?limit=100&offset=offset"]
B --> C{HTTP 200 OK?}
C -- No --> D["Print the error message and stop"]
C -- Yes --> E["Read meta.count"]
E --> F["Append the records to the collection"]
F --> G{"offset + limit < meta.count?"}
G -- Yes --> H["offset = offset + limit"]
H --> B
G -- No --> I["All pages done → write CSV"]
Note: a
meta.count-based loop was preferred over “loop until an empty page arrives”. The reason: if the data changes during pagination, the empty-page method can skip the last page or fire an extra request.meta.countis computed once in the loop and stays fixed.
The delivered tool
C# console application: submissions/02-liste-ve-filtreleme/Hipcall.MissedCalls/
It fetches the last 7 days of missed calls and writes them to CSV. Run it with dotnet run. The API key is read from the HIPCALL_API_TOKEN environment variable.
Ask the Community
Have questions or want to share your integration? Join the discussion.
Tools & APIs
Six ways to extend and integrate Hipcall into your systems.
REST API
Access your Hipcall data programmatically. Manage extensions, initiate calls, retrieve call records, and more via a RESTful HTTP API with OAuth 2.0 and API key authentication.
Webhooks
Receive real-time HTTP notifications for 13+ event types — incoming calls, answered calls, hangups, voicemails, and more. Push call data into any system instantly.
Web Service-Based Smart IVRs
Route incoming calls dynamically using your own business logic. Hipcall calls your web service at ring time and routes the call based on your JSON response.
Quick Call
Trigger outbound calls programmatically. Initiate a call from an agent's extension to any number via a single API request — great for click-to-call integrations.
External Management
Manage your Hipcall account from external systems. Provision users, update call flows, and control settings without logging into the dashboard.
Insight Card
Display real-time caller context on the agent's screen the moment a call connects. Push any data — name, company, account balance — from your CRM or ERP via API.