Pin a comment on a theme's preview
Creates a comment. Authorship comes from the authenticated principal, so there is no author field to send.
The author also owns the new comment: it comes back assigned to them, by
them, with assigned_at_ms 0. That stamp marks a default rather than a
decision, so any assignment made afterwards replaces it whatever the
writer’s clock reads. The same rule as any assignment applies, so an
author who is not an admin of this company leaves it unassigned.
Idempotent on client_id: repeating a request returns the stored comment
rather than creating a second one. template_id is required and must
belong to this theme — the rendered page carries the identity, so a
client that cannot supply it has not resolved what it is commenting on.
curl --request POST \
--url https://api.fluid.app/api/v202604/themes/{theme_id}/preview_comments \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"preview_comment": {
"client_id": "c-3f9a2e71",
"body": "The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?",
"template_id": 902114,
"page_path": "/",
"pin": {
"x": 48.2,
"y": 22.7,
"ox": 0.41,
"oy": 0.63
},
"section": {
"id": "hero-banner-1",
"type": "hero-banner"
},
"anchor": {
"tag": "h1",
"selector": "section[data-section-id=\"hero-banner-1\"] h1.hero__title",
"text": "Limitless Paper in a Paperless World",
"html": "<h1 class=\"hero__title\">Limitless Paper in a Paperless World</h1>"
},
"mentions": [
{
"id": "52018",
"name": "Pam Beesly"
}
]
}
}
'import requests
url = "https://api.fluid.app/api/v202604/themes/{theme_id}/preview_comments"
payload = { "preview_comment": {
"client_id": "c-3f9a2e71",
"body": "The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?",
"template_id": 902114,
"page_path": "/",
"pin": {
"x": 48.2,
"y": 22.7,
"ox": 0.41,
"oy": 0.63
},
"section": {
"id": "hero-banner-1",
"type": "hero-banner"
},
"anchor": {
"tag": "h1",
"selector": "section[data-section-id=\"hero-banner-1\"] h1.hero__title",
"text": "Limitless Paper in a Paperless World",
"html": "<h1 class=\"hero__title\">Limitless Paper in a Paperless World</h1>"
},
"mentions": [
{
"id": "52018",
"name": "Pam Beesly"
}
]
} }
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({
preview_comment: {
client_id: 'c-3f9a2e71',
body: JSON.stringify('The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?'),
template_id: 902114,
page_path: '/',
pin: {x: 48.2, y: 22.7, ox: 0.41, oy: 0.63},
section: {id: 'hero-banner-1', type: 'hero-banner'},
anchor: {
tag: 'h1',
selector: 'section[data-section-id="hero-banner-1"] h1.hero__title',
text: 'Limitless Paper in a Paperless World',
html: '<h1 class="hero__title">Limitless Paper in a Paperless World</h1>'
},
mentions: [{id: '52018', name: 'Pam Beesly'}]
}
})
};
fetch('https://api.fluid.app/api/v202604/themes/{theme_id}/preview_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://api.fluid.app/api/v202604/themes/{theme_id}/preview_comments",
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([
'preview_comment' => [
'client_id' => 'c-3f9a2e71',
'body' => 'The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?',
'template_id' => 902114,
'page_path' => '/',
'pin' => [
'x' => 48.2,
'y' => 22.7,
'ox' => 0.41,
'oy' => 0.63
],
'section' => [
'id' => 'hero-banner-1',
'type' => 'hero-banner'
],
'anchor' => [
'tag' => 'h1',
'selector' => 'section[data-section-id="hero-banner-1"] h1.hero__title',
'text' => 'Limitless Paper in a Paperless World',
'html' => '<h1 class="hero__title">Limitless Paper in a Paperless World</h1>'
],
'mentions' => [
[
'id' => '52018',
'name' => 'Pam Beesly'
]
]
]
]),
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.fluid.app/api/v202604/themes/{theme_id}/preview_comments"
payload := strings.NewReader("{\n \"preview_comment\": {\n \"client_id\": \"c-3f9a2e71\",\n \"body\": \"The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?\",\n \"template_id\": 902114,\n \"page_path\": \"/\",\n \"pin\": {\n \"x\": 48.2,\n \"y\": 22.7,\n \"ox\": 0.41,\n \"oy\": 0.63\n },\n \"section\": {\n \"id\": \"hero-banner-1\",\n \"type\": \"hero-banner\"\n },\n \"anchor\": {\n \"tag\": \"h1\",\n \"selector\": \"section[data-section-id=\\\"hero-banner-1\\\"] h1.hero__title\",\n \"text\": \"Limitless Paper in a Paperless World\",\n \"html\": \"<h1 class=\\\"hero__title\\\">Limitless Paper in a Paperless World</h1>\"\n },\n \"mentions\": [\n {\n \"id\": \"52018\",\n \"name\": \"Pam Beesly\"\n }\n ]\n }\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.fluid.app/api/v202604/themes/{theme_id}/preview_comments")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"preview_comment\": {\n \"client_id\": \"c-3f9a2e71\",\n \"body\": \"The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?\",\n \"template_id\": 902114,\n \"page_path\": \"/\",\n \"pin\": {\n \"x\": 48.2,\n \"y\": 22.7,\n \"ox\": 0.41,\n \"oy\": 0.63\n },\n \"section\": {\n \"id\": \"hero-banner-1\",\n \"type\": \"hero-banner\"\n },\n \"anchor\": {\n \"tag\": \"h1\",\n \"selector\": \"section[data-section-id=\\\"hero-banner-1\\\"] h1.hero__title\",\n \"text\": \"Limitless Paper in a Paperless World\",\n \"html\": \"<h1 class=\\\"hero__title\\\">Limitless Paper in a Paperless World</h1>\"\n },\n \"mentions\": [\n {\n \"id\": \"52018\",\n \"name\": \"Pam Beesly\"\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/v202604/themes/{theme_id}/preview_comments")
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 \"preview_comment\": {\n \"client_id\": \"c-3f9a2e71\",\n \"body\": \"The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?\",\n \"template_id\": 902114,\n \"page_path\": \"/\",\n \"pin\": {\n \"x\": 48.2,\n \"y\": 22.7,\n \"ox\": 0.41,\n \"oy\": 0.63\n },\n \"section\": {\n \"id\": \"hero-banner-1\",\n \"type\": \"hero-banner\"\n },\n \"anchor\": {\n \"tag\": \"h1\",\n \"selector\": \"section[data-section-id=\\\"hero-banner-1\\\"] h1.hero__title\",\n \"text\": \"Limitless Paper in a Paperless World\",\n \"html\": \"<h1 class=\\\"hero__title\\\">Limitless Paper in a Paperless World</h1>\"\n },\n \"mentions\": [\n {\n \"id\": \"52018\",\n \"name\": \"Pam Beesly\"\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"preview_comment": {
"id": 31877,
"run_id": null,
"lock_version": 0,
"client_id": "c-3f9a2e71",
"n": 4,
"body": "The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?",
"status": "open",
"status_at_ms": 1789398131000,
"page_url": "/",
"author_name": "Michael Scott",
"created_at_ms": 1789398131000,
"theme_changed_at": null,
"template_id": 902114,
"section": {
"id": "hero-banner-1",
"type": "hero-banner"
},
"block": null,
"target": {
"tag": "h1",
"selector": "section[data-section-id=\"hero-banner-1\"] h1.hero__title",
"text": "Limitless Paper in a Paperless World",
"html": "<h1 class=\"hero__title\">Limitless Paper in a Paperless World</h1>"
},
"pin": {
"x": 48.2,
"y": 22.7,
"ox": 0.41,
"oy": 0.63
},
"mentions": [
{
"id": "52018",
"name": "Pam Beesly"
}
],
"mentioned_project": null,
"proof_baseline": null,
"work_item": null,
"board_position": null,
"board_position_at_ms": null,
"assignee": {
"id": "52001",
"name": "Michael Scott",
"kind": "user"
},
"assigner": {
"id": "52001",
"name": "Michael Scott",
"kind": "user"
},
"assigned_at_ms": 0,
"priority": null,
"priority_at_ms": null,
"replies": []
},
"status": 201,
"meta": {
"request_uuid": "7c1e9a52-4b3d-4f0e-9d2a-81f6c3b5e240",
"timestamp": "2026-09-14T15:02:11Z"
}
}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}Authorizations
Body
Response
The stored comment.
A single comment, wrapped in the standard response envelope.
curl --request POST \
--url https://api.fluid.app/api/v202604/themes/{theme_id}/preview_comments \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"preview_comment": {
"client_id": "c-3f9a2e71",
"body": "The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?",
"template_id": 902114,
"page_path": "/",
"pin": {
"x": 48.2,
"y": 22.7,
"ox": 0.41,
"oy": 0.63
},
"section": {
"id": "hero-banner-1",
"type": "hero-banner"
},
"anchor": {
"tag": "h1",
"selector": "section[data-section-id=\"hero-banner-1\"] h1.hero__title",
"text": "Limitless Paper in a Paperless World",
"html": "<h1 class=\"hero__title\">Limitless Paper in a Paperless World</h1>"
},
"mentions": [
{
"id": "52018",
"name": "Pam Beesly"
}
]
}
}
'import requests
url = "https://api.fluid.app/api/v202604/themes/{theme_id}/preview_comments"
payload = { "preview_comment": {
"client_id": "c-3f9a2e71",
"body": "The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?",
"template_id": 902114,
"page_path": "/",
"pin": {
"x": 48.2,
"y": 22.7,
"ox": 0.41,
"oy": 0.63
},
"section": {
"id": "hero-banner-1",
"type": "hero-banner"
},
"anchor": {
"tag": "h1",
"selector": "section[data-section-id=\"hero-banner-1\"] h1.hero__title",
"text": "Limitless Paper in a Paperless World",
"html": "<h1 class=\"hero__title\">Limitless Paper in a Paperless World</h1>"
},
"mentions": [
{
"id": "52018",
"name": "Pam Beesly"
}
]
} }
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({
preview_comment: {
client_id: 'c-3f9a2e71',
body: JSON.stringify('The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?'),
template_id: 902114,
page_path: '/',
pin: {x: 48.2, y: 22.7, ox: 0.41, oy: 0.63},
section: {id: 'hero-banner-1', type: 'hero-banner'},
anchor: {
tag: 'h1',
selector: 'section[data-section-id="hero-banner-1"] h1.hero__title',
text: 'Limitless Paper in a Paperless World',
html: '<h1 class="hero__title">Limitless Paper in a Paperless World</h1>'
},
mentions: [{id: '52018', name: 'Pam Beesly'}]
}
})
};
fetch('https://api.fluid.app/api/v202604/themes/{theme_id}/preview_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://api.fluid.app/api/v202604/themes/{theme_id}/preview_comments",
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([
'preview_comment' => [
'client_id' => 'c-3f9a2e71',
'body' => 'The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?',
'template_id' => 902114,
'page_path' => '/',
'pin' => [
'x' => 48.2,
'y' => 22.7,
'ox' => 0.41,
'oy' => 0.63
],
'section' => [
'id' => 'hero-banner-1',
'type' => 'hero-banner'
],
'anchor' => [
'tag' => 'h1',
'selector' => 'section[data-section-id="hero-banner-1"] h1.hero__title',
'text' => 'Limitless Paper in a Paperless World',
'html' => '<h1 class="hero__title">Limitless Paper in a Paperless World</h1>'
],
'mentions' => [
[
'id' => '52018',
'name' => 'Pam Beesly'
]
]
]
]),
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.fluid.app/api/v202604/themes/{theme_id}/preview_comments"
payload := strings.NewReader("{\n \"preview_comment\": {\n \"client_id\": \"c-3f9a2e71\",\n \"body\": \"The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?\",\n \"template_id\": 902114,\n \"page_path\": \"/\",\n \"pin\": {\n \"x\": 48.2,\n \"y\": 22.7,\n \"ox\": 0.41,\n \"oy\": 0.63\n },\n \"section\": {\n \"id\": \"hero-banner-1\",\n \"type\": \"hero-banner\"\n },\n \"anchor\": {\n \"tag\": \"h1\",\n \"selector\": \"section[data-section-id=\\\"hero-banner-1\\\"] h1.hero__title\",\n \"text\": \"Limitless Paper in a Paperless World\",\n \"html\": \"<h1 class=\\\"hero__title\\\">Limitless Paper in a Paperless World</h1>\"\n },\n \"mentions\": [\n {\n \"id\": \"52018\",\n \"name\": \"Pam Beesly\"\n }\n ]\n }\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.fluid.app/api/v202604/themes/{theme_id}/preview_comments")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"preview_comment\": {\n \"client_id\": \"c-3f9a2e71\",\n \"body\": \"The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?\",\n \"template_id\": 902114,\n \"page_path\": \"/\",\n \"pin\": {\n \"x\": 48.2,\n \"y\": 22.7,\n \"ox\": 0.41,\n \"oy\": 0.63\n },\n \"section\": {\n \"id\": \"hero-banner-1\",\n \"type\": \"hero-banner\"\n },\n \"anchor\": {\n \"tag\": \"h1\",\n \"selector\": \"section[data-section-id=\\\"hero-banner-1\\\"] h1.hero__title\",\n \"text\": \"Limitless Paper in a Paperless World\",\n \"html\": \"<h1 class=\\\"hero__title\\\">Limitless Paper in a Paperless World</h1>\"\n },\n \"mentions\": [\n {\n \"id\": \"52018\",\n \"name\": \"Pam Beesly\"\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/v202604/themes/{theme_id}/preview_comments")
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 \"preview_comment\": {\n \"client_id\": \"c-3f9a2e71\",\n \"body\": \"The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?\",\n \"template_id\": 902114,\n \"page_path\": \"/\",\n \"pin\": {\n \"x\": 48.2,\n \"y\": 22.7,\n \"ox\": 0.41,\n \"oy\": 0.63\n },\n \"section\": {\n \"id\": \"hero-banner-1\",\n \"type\": \"hero-banner\"\n },\n \"anchor\": {\n \"tag\": \"h1\",\n \"selector\": \"section[data-section-id=\\\"hero-banner-1\\\"] h1.hero__title\",\n \"text\": \"Limitless Paper in a Paperless World\",\n \"html\": \"<h1 class=\\\"hero__title\\\">Limitless Paper in a Paperless World</h1>\"\n },\n \"mentions\": [\n {\n \"id\": \"52018\",\n \"name\": \"Pam Beesly\"\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"preview_comment": {
"id": 31877,
"run_id": null,
"lock_version": 0,
"client_id": "c-3f9a2e71",
"n": 4,
"body": "The hero headline wraps onto three lines on mobile. Can we tighten it so it fits on two?",
"status": "open",
"status_at_ms": 1789398131000,
"page_url": "/",
"author_name": "Michael Scott",
"created_at_ms": 1789398131000,
"theme_changed_at": null,
"template_id": 902114,
"section": {
"id": "hero-banner-1",
"type": "hero-banner"
},
"block": null,
"target": {
"tag": "h1",
"selector": "section[data-section-id=\"hero-banner-1\"] h1.hero__title",
"text": "Limitless Paper in a Paperless World",
"html": "<h1 class=\"hero__title\">Limitless Paper in a Paperless World</h1>"
},
"pin": {
"x": 48.2,
"y": 22.7,
"ox": 0.41,
"oy": 0.63
},
"mentions": [
{
"id": "52018",
"name": "Pam Beesly"
}
],
"mentioned_project": null,
"proof_baseline": null,
"work_item": null,
"board_position": null,
"board_position_at_ms": null,
"assignee": {
"id": "52001",
"name": "Michael Scott",
"kind": "user"
},
"assigner": {
"id": "52001",
"name": "Michael Scott",
"kind": "user"
},
"assigned_at_ms": 0,
"priority": null,
"priority_at_ms": null,
"replies": []
},
"status": 201,
"meta": {
"request_uuid": "7c1e9a52-4b3d-4f0e-9d2a-81f6c3b5e240",
"timestamp": "2026-09-14T15:02:11Z"
}
}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}