curl --request GET \
--url https://brew.new/api/v1/emails/{emailId}/comments \
--header 'Authorization: Bearer <token>'import requests
url = "https://brew.new/api/v1/emails/{emailId}/comments"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://brew.new/api/v1/emails/{emailId}/comments', 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://brew.new/api/v1/emails/{emailId}/comments",
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://brew.new/api/v1/emails/{emailId}/comments"
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://brew.new/api/v1/emails/{emailId}/comments")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://brew.new/api/v1/emails/{emailId}/comments")
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{
"data": [
{
"commentId": "cmt_V1StGXR8_Z5jdHi6B-myT",
"emailId": "2SmZOWV3ZQ7W5x6g3m4pA",
"status": "open",
"target": {
"kind": "element",
"elementId": "elm_4f9Kq",
"nodeType": "button"
},
"participants": [
{
"userId": "user_2xK9mPq4Rt7Vw1Yb",
"name": "Grace Hopper"
},
{
"userId": "user_2abcQ8sLm3Nd5Tz",
"name": "Ada Lovelace"
}
],
"participantCount": 2,
"messageCount": 3,
"lastMessageAt": "2026-10-01T12:05:00.000Z",
"lastMessagePreview": "Bolder works. Shipping it.",
"url": "https://brew.new/emails/ungrouped?emailIds=2SmZOWV3ZQ7W5x6g3m4pA&focusEmailIds=2SmZOWV3ZQ7W5x6g3m4pA&commentId=cmt_V1StGXR8_Z5jdHi6B-myT",
"messages": [
{
"messageId": "cmm_9pQ2wErT5yU8iO1aSdF3g",
"author": {
"userId": "user_2xK9mPq4Rt7Vw1Yb",
"name": "Grace Hopper"
},
"body": "@Ada Lovelace can the CTA be bolder?",
"mentions": [
{
"userId": "user_2abcQ8sLm3Nd5Tz",
"name": "Ada Lovelace"
}
],
"createdAt": "2026-10-01T12:00:00.000Z",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
{
"messageId": "cmm_3hJ6kL9zX2cV5bN8mQ1wE",
"author": {
"userId": "user_2abcQ8sLm3Nd5Tz",
"name": "Ada Lovelace"
},
"body": "Bolder works. Shipping it.",
"mentions": [],
"createdAt": "2026-10-01T12:05:00.000Z",
"updatedAt": "2026-10-01T12:05:00.000Z"
}
],
"messagesCursor": "eyJ2IjoxLCJjIjoiY210X1YxU3RHWFI4X1o1amRIaTZCLW15VCIsImEiOiIyMDI2LTEwLTAxVDEyOjAwOjAwLjAwMFoiLCJtIjoiY21tXzlwUTJ3RXJUNXlVOGlPMWFTZEYzZyJ9"
}
],
"pagination": {
"limit": 3,
"cursor": null,
"hasMore": false
}
}List email comments
Lists one design’s open comment threads, newest activity first, under { data, pagination }, as the canvas pins show them: commentId, target (whole email or one element), the 12 most recent participants with participantCount, messageCount, the latest message preview and a link to the thread. People are { userId, name }; resolving deletes a thread. Free.
Use when reading what teammates asked for on a design before editing it.
Input emailId in the path; limit and cursor. include=messages adds each thread’s newest messages (author, body, mentions), oldest first, and caps the page at 3 threads sharing a fixed size budget. A thread with older messages returns messagesCursor: send it back with that thread’s commentId to read the next older slice (the whole budget, at least one message), until it comes back null. commentId alone reads one thread.
Returns 200 with a page of EmailCommentThread rows. A design with no threads is an empty page.
Errors 400 INVALID_REQUEST for an unknown query key, an unknown include token, a malformed cursor, a messagesCursor without its commentId or from another thread, or cursor with commentId. 404 EMAIL_NOT_FOUND for an emailId the brand does not have: unknown, malformed or another brand’s design, one identical error as getEmail answers. 404 COMMENT_NOT_FOUND for a commentId that is not an open thread on that design (resolving deletes a thread, so a resolved one is the same case).
See also getEmail.
curl --request GET \
--url https://brew.new/api/v1/emails/{emailId}/comments \
--header 'Authorization: Bearer <token>'import requests
url = "https://brew.new/api/v1/emails/{emailId}/comments"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://brew.new/api/v1/emails/{emailId}/comments', 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://brew.new/api/v1/emails/{emailId}/comments",
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://brew.new/api/v1/emails/{emailId}/comments"
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://brew.new/api/v1/emails/{emailId}/comments")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://brew.new/api/v1/emails/{emailId}/comments")
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{
"data": [
{
"commentId": "cmt_V1StGXR8_Z5jdHi6B-myT",
"emailId": "2SmZOWV3ZQ7W5x6g3m4pA",
"status": "open",
"target": {
"kind": "element",
"elementId": "elm_4f9Kq",
"nodeType": "button"
},
"participants": [
{
"userId": "user_2xK9mPq4Rt7Vw1Yb",
"name": "Grace Hopper"
},
{
"userId": "user_2abcQ8sLm3Nd5Tz",
"name": "Ada Lovelace"
}
],
"participantCount": 2,
"messageCount": 3,
"lastMessageAt": "2026-10-01T12:05:00.000Z",
"lastMessagePreview": "Bolder works. Shipping it.",
"url": "https://brew.new/emails/ungrouped?emailIds=2SmZOWV3ZQ7W5x6g3m4pA&focusEmailIds=2SmZOWV3ZQ7W5x6g3m4pA&commentId=cmt_V1StGXR8_Z5jdHi6B-myT",
"messages": [
{
"messageId": "cmm_9pQ2wErT5yU8iO1aSdF3g",
"author": {
"userId": "user_2xK9mPq4Rt7Vw1Yb",
"name": "Grace Hopper"
},
"body": "@Ada Lovelace can the CTA be bolder?",
"mentions": [
{
"userId": "user_2abcQ8sLm3Nd5Tz",
"name": "Ada Lovelace"
}
],
"createdAt": "2026-10-01T12:00:00.000Z",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
{
"messageId": "cmm_3hJ6kL9zX2cV5bN8mQ1wE",
"author": {
"userId": "user_2abcQ8sLm3Nd5Tz",
"name": "Ada Lovelace"
},
"body": "Bolder works. Shipping it.",
"mentions": [],
"createdAt": "2026-10-01T12:05:00.000Z",
"updatedAt": "2026-10-01T12:05:00.000Z"
}
],
"messagesCursor": "eyJ2IjoxLCJjIjoiY210X1YxU3RHWFI4X1o1amRIaTZCLW15VCIsImEiOiIyMDI2LTEwLTAxVDEyOjAwOjAwLjAwMFoiLCJtIjoiY21tXzlwUTJ3RXJUNXlVOGlPMWFTZEYzZyJ9"
}
],
"pagination": {
"limit": 3,
"cursor": null,
"hasMore": false
}
}Authorizations
Send your Brew API key as Authorization: Bearer brew_xxx.
Headers
The brand this request acts on. REQUIRED for organization-scoped credentials (otherwise 400 BRAND_ID_REQUIRED — there is no default brand); list ids with GET /v1/brands. Brand-scoped credentials may omit it, and sending a different brand returns 403 BRAND_SCOPE_MISMATCH. A brand outside your organization returns 404 BRAND_NOT_FOUND.
1 - 64Path Parameters
Design id returned by POST /v1/emails and listed by GET /v1/emails.
1 - 64"2SmZOWV3ZQ7W5x6g3m4pA"
Query Parameters
Comma-separated expansions: messages adds each thread's newest messages (author, body, mentions), oldest first, with a messagesCursor for older ones, and caps the page at 3 threads.
Read this one thread (cmt_…) instead of the page; not with cursor.
5 - 64A thread's messagesCursor from a previous response, with that thread's commentId: returns the thread with its next older messages and the next messagesCursor. Implies include=messages.
1 - 512Page size (1-100). Defaults to 100.
1 <= x <= 10050
1 - 512Was this page helpful?