curl --request POST \
--url https://api.example.com/deals/{deal_id}/activities \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"type": "note",
"summary": "<string>",
"body": "<string>",
"direction": "<string>",
"contact_id": "<string>",
"due_at": "<string>",
"duration_minutes": 123,
"attendees": [
{
"name": "<string>",
"email": "<string>"
}
],
"transcript": "<string>",
"recording_url": "<string>",
"location": "<string>",
"provider": "<string>"
}
'import requests
url = "https://api.example.com/deals/{deal_id}/activities"
payload = {
"type": "note",
"summary": "<string>",
"body": "<string>",
"direction": "<string>",
"contact_id": "<string>",
"due_at": "<string>",
"duration_minutes": 123,
"attendees": [
{
"name": "<string>",
"email": "<string>"
}
],
"transcript": "<string>",
"recording_url": "<string>",
"location": "<string>",
"provider": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
type: 'note',
summary: '<string>',
body: JSON.stringify('<string>'),
direction: '<string>',
contact_id: '<string>',
due_at: '<string>',
duration_minutes: 123,
attendees: [{name: '<string>', email: '<string>'}],
transcript: '<string>',
recording_url: '<string>',
location: '<string>',
provider: '<string>'
})
};
fetch('https://api.example.com/deals/{deal_id}/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/deals/{deal_id}/activities",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'type' => 'note',
'summary' => '<string>',
'body' => '<string>',
'direction' => '<string>',
'contact_id' => '<string>',
'due_at' => '<string>',
'duration_minutes' => 123,
'attendees' => [
[
'name' => '<string>',
'email' => '<string>'
]
],
'transcript' => '<string>',
'recording_url' => '<string>',
'location' => '<string>',
'provider' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/deals/{deal_id}/activities"
payload := strings.NewReader("{\n \"type\": \"note\",\n \"summary\": \"<string>\",\n \"body\": \"<string>\",\n \"direction\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"due_at\": \"<string>\",\n \"duration_minutes\": 123,\n \"attendees\": [\n {\n \"name\": \"<string>\",\n \"email\": \"<string>\"\n }\n ],\n \"transcript\": \"<string>\",\n \"recording_url\": \"<string>\",\n \"location\": \"<string>\",\n \"provider\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/deals/{deal_id}/activities")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"type\": \"note\",\n \"summary\": \"<string>\",\n \"body\": \"<string>\",\n \"direction\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"due_at\": \"<string>\",\n \"duration_minutes\": 123,\n \"attendees\": [\n {\n \"name\": \"<string>\",\n \"email\": \"<string>\"\n }\n ],\n \"transcript\": \"<string>\",\n \"recording_url\": \"<string>\",\n \"location\": \"<string>\",\n \"provider\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/deals/{deal_id}/activities")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"type\": \"note\",\n \"summary\": \"<string>\",\n \"body\": \"<string>\",\n \"direction\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"due_at\": \"<string>\",\n \"duration_minutes\": 123,\n \"attendees\": [\n {\n \"name\": \"<string>\",\n \"email\": \"<string>\"\n }\n ],\n \"transcript\": \"<string>\",\n \"recording_url\": \"<string>\",\n \"location\": \"<string>\",\n \"provider\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Log an Activity
curl --request POST \
--url https://api.example.com/deals/{deal_id}/activities \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"type": "note",
"summary": "<string>",
"body": "<string>",
"direction": "<string>",
"contact_id": "<string>",
"due_at": "<string>",
"duration_minutes": 123,
"attendees": [
{
"name": "<string>",
"email": "<string>"
}
],
"transcript": "<string>",
"recording_url": "<string>",
"location": "<string>",
"provider": "<string>"
}
'import requests
url = "https://api.example.com/deals/{deal_id}/activities"
payload = {
"type": "note",
"summary": "<string>",
"body": "<string>",
"direction": "<string>",
"contact_id": "<string>",
"due_at": "<string>",
"duration_minutes": 123,
"attendees": [
{
"name": "<string>",
"email": "<string>"
}
],
"transcript": "<string>",
"recording_url": "<string>",
"location": "<string>",
"provider": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
type: 'note',
summary: '<string>',
body: JSON.stringify('<string>'),
direction: '<string>',
contact_id: '<string>',
due_at: '<string>',
duration_minutes: 123,
attendees: [{name: '<string>', email: '<string>'}],
transcript: '<string>',
recording_url: '<string>',
location: '<string>',
provider: '<string>'
})
};
fetch('https://api.example.com/deals/{deal_id}/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/deals/{deal_id}/activities",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'type' => 'note',
'summary' => '<string>',
'body' => '<string>',
'direction' => '<string>',
'contact_id' => '<string>',
'due_at' => '<string>',
'duration_minutes' => 123,
'attendees' => [
[
'name' => '<string>',
'email' => '<string>'
]
],
'transcript' => '<string>',
'recording_url' => '<string>',
'location' => '<string>',
'provider' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/deals/{deal_id}/activities"
payload := strings.NewReader("{\n \"type\": \"note\",\n \"summary\": \"<string>\",\n \"body\": \"<string>\",\n \"direction\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"due_at\": \"<string>\",\n \"duration_minutes\": 123,\n \"attendees\": [\n {\n \"name\": \"<string>\",\n \"email\": \"<string>\"\n }\n ],\n \"transcript\": \"<string>\",\n \"recording_url\": \"<string>\",\n \"location\": \"<string>\",\n \"provider\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/deals/{deal_id}/activities")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"type\": \"note\",\n \"summary\": \"<string>\",\n \"body\": \"<string>\",\n \"direction\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"due_at\": \"<string>\",\n \"duration_minutes\": 123,\n \"attendees\": [\n {\n \"name\": \"<string>\",\n \"email\": \"<string>\"\n }\n ],\n \"transcript\": \"<string>\",\n \"recording_url\": \"<string>\",\n \"location\": \"<string>\",\n \"provider\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/deals/{deal_id}/activities")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"type\": \"note\",\n \"summary\": \"<string>\",\n \"body\": \"<string>\",\n \"direction\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"due_at\": \"<string>\",\n \"duration_minutes\": 123,\n \"attendees\": [\n {\n \"name\": \"<string>\",\n \"email\": \"<string>\"\n }\n ],\n \"transcript\": \"<string>\",\n \"recording_url\": \"<string>\",\n \"location\": \"<string>\",\n \"provider\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}ActivityBody shape also used on Accounts and Activities. The meeting-flavoured fields are persisted for every activity type — nothing branches on type. attendees, transcript, recording_url, location and provider collapse into the activities.metadata JSONB column; due_at and duration_minutes are not metadata — they go into their own activities.due_at / activities.duration_minutes columns.
transcript sent here lands in activities.metadata.transcript, which nothing in the backend reads. Generate Call Intelligence works off the transcript_text column (falling back to the activity body), and List Meetings derives has_transcript from that same column — so a transcript supplied through this endpoint is invisible to both. Put the text in body if you need Call Intelligence to see it.Auth
Requires a CRM manage scope and an active organization on the token. Any*:manage scope qualifies — in practice contacts:manage, deals:manage, companies:manage, or activities:manage.
Response
201 with {"id": "<activity id>"}. Also records a track_activity_logged analytics event.Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
The deal's id.
Body
Activity type: note, call, meeting, email, or task.
Short headline for the activity; stored as the subject column.
Free-text body/notes content.
Direction for calls/emails, e.g. inbound or outbound.
Contact this activity is associated with.
ISO 8601 scheduled time. Persisted for any activity type, not just meetings, and it drives the generated effective_time column (COALESCE(due_at, created_at)) that the activity feed sorts by and that exclude_future filters on.
Duration in minutes. Persisted for any activity type, not just meetings.
Attendee list, stored in metadata. Persisted for any activity type, not just meetings.
Show child attributes
Show child attributes
Transcript text, stored in metadata. Persisted for any activity type, not just meetings.
Absolute http(s) URL to a recording, stored in metadata. Persisted for any activity type, not just meetings.
Location or video-call link, stored in metadata. Persisted for any activity type, not just meetings.
Source provider, e.g. gcal, zoom, teams, or manual, stored in metadata. Persisted for any activity type, not just meetings.
Response
Successful Response