curl --request GET \
--url https://api.example.com/activities \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.example.com/activities"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.example.com/activities', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/activities",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/activities"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.example.com/activities")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/activities")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body[
{}
]{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}List Activities (Global Feed)
Global activity feed for the active org with filters.
Returns activities of any type (meeting, message, email, call, note, etc.)
sorted by effective_time desc — a generated column = COALESCE(due_at, created_at).
This gives a single linear time axis regardless of activity type, so manual
notes don’t get pushed to the bottom just because they lack a due_at.
curl --request GET \
--url https://api.example.com/activities \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.example.com/activities"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.example.com/activities', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/activities",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/activities"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.example.com/activities")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/activities")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body[
{}
]{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}effective_time descending. effective_time is a generated column — COALESCE(due_at, created_at) computed by Postgres, so it can never be sent on a write — and sorting by it means manual notes (no due_at) don’t get pushed to the bottom of a timeline that’s otherwise ordered by scheduled time.
Returns a bare JSON array — no total-count header, no pagination envelope. limit is capped at 500 server-side. Note that the from/to filters constrain due_at, not effective_time.
unlinked_account=true together with an explicit account_id, or unlinked_deal=true with deal_id, sends the same PostgREST query key twice (eq.<id> and is.null), which is a self-contradicting AND — you get an empty result, not an error.Auth
Requires a CRM read scope and an active organization on the token. Any ofcontacts:read, deals:read, companies:read, or activities:read qualifies, as does any *:manage scope — manage implies read.
Response
Same row shape as List Meetings, excepttype varies per row instead of always being "meeting":
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Primary key. |
type | string | note, call, meeting, email, task, or a system-generated type. |
subject | string | null | Short headline. |
body | string | null | Free-text content. |
due_at | string | null (timestamptz) | Scheduled time. |
completed_at | string | null (timestamptz) | When the activity was marked done, if applicable. |
duration_minutes | integer | null | Meeting length; usually null for non-meeting types. |
metadata | object | null (jsonb) | Free-form; only meaningfully populated for meetings. See the List Meetings response for what manual vs. synced sources write into it. |
account_id | string | null (uuid) | Linked account. |
deal_id | string | null (uuid) | Linked deal. |
contact_id | string | null (uuid) | Linked contact. |
owner_id | string | null | Logto user sub of the owning rep. |
source | string | manual, import, or a sync source (calendar, gmail, outlook, slack, teams). |
direction | string | null | inbound | outbound, where applicable (e.g. calls, emails). |
created_at | string (timestamptz) | Row creation time. |
accounts | object | null | Embedded: { id, name, domain }. null if account_id is unset. |
deals | object | null | Embedded: { id, name, stage_id }. null if deal_id is unset. |
contacts | object | null | Embedded: { id, first_name, last_name, email }. null if contact_id is unset. |
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Query Parameters
Filter to activities of this type (e.g. note, call, meeting, email, task).
Filter to activities from this source (manual, import, or a sync source such as calendar, gmail, outlook, slack, teams).
Case-insensitive substring match against subject.
Filter to activities linked to this contact.
Filter to activities linked to this deal.
Filter to activities linked to this account.
If true, only return activities with no account_id. Combining with an explicit account_id filter yields an empty result.
If true, only return activities with no deal_id. Combining with an explicit deal_id filter yields an empty result.
If true, hide activities whose effective_time is later than the current UTC instant. Note this cuts at 'now', not at end of day — an activity due later today is excluded.
Only return activities with due_at on or after this ISO 8601 timestamp — filters due_at, not the effective_time the feed is sorted by.
Only return activities with due_at on or before this ISO 8601 timestamp — filters due_at, not the effective_time the feed is sorted by.
Maximum rows to return; capped at 500 server-side.
Number of rows to skip, for pagination.
Response
Successful Response