Calls and call logs
Log phone calls against Contacts, Deals and prospect contacts, and read your call history.
A call log records a phone call: who it was with, which way it went, how long it lasted and how it ended. Logged calls count in Signal's call history and call reporting.
These endpoints use the activities:read and activities:write scopes.
There are two ways to log a call. They take the same body and return the same object:
POST /calls(recommended).POST /call-logsis the original endpoint and is kept for existing integrations.
Both check that any linked Contact, Deal or prospect contact belongs to your workspace.
The call object#
| Field | Type | Notes |
|---|---|---|
id |
string | 25-character ID. |
subject |
string | |
direction |
string or null | outbound or inbound. |
duration_seconds |
integer or null | |
outcome |
string or null | Free text. |
notes |
string or null | |
contact_id |
string or null | |
deal_id |
string or null | |
prospect_id |
string or null | |
owner_id |
string or null | User ID of the person who made or took the call. |
owner_name |
string or null | That user's name. Read-only. |
created_at |
string | ISO 8601 timestamp. |
updated_at |
string | ISO 8601 timestamp. |
{
"id": "cm3ka1m4s0002ab7c9d1e3f5g",
"subject": "Discovery call with Maria Garcia",
"direction": "outbound",
"duration_seconds": 754,
"outcome": "connected",
"notes": "Wants pricing for 250 seats.",
"contact_id": "a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54",
"deal_id": "7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81",
"prospect_id": null,
"owner_id": "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20",
"owner_name": "Jordan Smith",
"created_at": "2026-11-12T09:30:00.000Z",
"updated_at": "2026-11-12T09:30:00.000Z"
}
List calls#
GET /calls · Scope: activities:read
Returns calls newest first.
Query parameters#
| Parameter | Type | Required | Notes |
|---|---|---|---|
contact_id |
string | No | |
deal_id |
string | No | |
prospect_id |
string | No | |
owner_id |
string | No | |
limit |
integer | No | 1 to 100. Default 20. |
cursor |
string | No | meta.cursor from the previous page. |
Example#
curl "https://crm.empiraa.app/api/v1/calls?owner_id=5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20" \
-H "Authorization: Bearer YOUR_API_KEY"
const url = new URL("https://crm.empiraa.app/api/v1/calls");
url.searchParams.set("owner_id", "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20");
const res = await fetch(url, { headers: { Authorization: "Bearer YOUR_API_KEY" } });
const { data, meta } = await res.json();
Response 200#
An array of call objects in data; meta has cursor and hasMore.
Log a call#
POST /calls · Scope: activities:write · Supports Idempotency-Key
Request body#
| Field | Type | Required | Notes |
|---|---|---|---|
subject |
string | Yes | |
direction |
string or null | No | outbound or inbound. |
duration_seconds |
integer or null | No | Ignored unless it's a number. |
outcome |
string or null | No | |
notes |
string or null | No | |
contact_id |
string or null | No | Must be a Contact in this workspace. |
deal_id |
string or null | No | Must be a Deal in this workspace. |
prospect_id |
string or null | No | Must be a prospect contact in this workspace. |
owner_id |
string or null | No | A member of the workspace. Leave out and the call has no owner, so it isn't credited to anyone. |
Example#
curl -X POST https://crm.empiraa.app/api/v1/calls \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: call-20261112-0930-a1d4e7b2" \
-d '{
"subject": "Discovery call with Maria Garcia",
"direction": "outbound",
"duration_seconds": 754,
"outcome": "connected",
"contact_id": "a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54",
"owner_id": "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20"
}'
const res = await fetch("https://crm.empiraa.app/api/v1/calls", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
subject: "Discovery call with Maria Garcia",
direction: "outbound",
duration_seconds: 754,
outcome: "connected",
contact_id: "a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54",
owner_id: "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20",
}),
});
Response 201#
The new call object in data.
Errors#
| Status | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST |
subject is missing, or owner_id isn't a string or null. |
| 404 | NOT_FOUND |
A linked Contact, Deal or prospect contact isn't in this workspace, or owner_id isn't a member of the workspace. |
Webhook events: none.
Get a call#
GET /calls/{id} · Scope: activities:read
Example#
curl https://crm.empiraa.app/api/v1/calls/cm3ka1m4s0002ab7c9d1e3f5g \
-H "Authorization: Bearer YOUR_API_KEY"
await fetch("https://crm.empiraa.app/api/v1/calls/cm3ka1m4s0002ab7c9d1e3f5g", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
Response 200#
The call object in data.
Errors#
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
No call with that ID in this workspace. |
Log a call (original endpoint)#
POST /call-logs · Scope: activities:write · Supports Idempotency-Key
Takes the same body as POST /calls and returns the same 201 response. It doesn't check that linked records belong to your workspace, so prefer POST /calls for new integrations. /call-logs accepts only POST.
Example#
curl -X POST https://crm.empiraa.app/api/v1/call-logs \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"subject": "Follow-up call", "direction": "inbound", "duration_seconds": 180}'
await fetch("https://crm.empiraa.app/api/v1/call-logs", {
method: "POST",
headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ subject: "Follow-up call", direction: "inbound", duration_seconds: 180 }),
});
Errors#
| Status | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST |
subject is missing, or owner_id isn't a string or null. |
| 404 | NOT_FOUND |
A linked Contact, Deal or prospect contact isn't in this workspace, or owner_id isn't a member of the workspace. |
Webhook events: none.

