curl --request POST \
--url https://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Team-SID: <api-key>' \
--data '
{
"linkedin_account_sid": "<string>",
"entity_urn": "<string>",
"text": "<string>",
"parent_comment_urn": "<string>",
"mentions": [
{
"profile_id": "<string>",
"name": "<string>"
}
],
"client_reference": "<string>",
"confirmed_not_sent": "<string>",
"confirmed_sent": "<string>"
}
'import requests
url = "https://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment"
payload = {
"linkedin_account_sid": "<string>",
"entity_urn": "<string>",
"text": "<string>",
"parent_comment_urn": "<string>",
"mentions": [
{
"profile_id": "<string>",
"name": "<string>"
}
],
"client_reference": "<string>",
"confirmed_not_sent": "<string>",
"confirmed_sent": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Team-SID": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: 'Bearer <token>',
'Team-SID': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
linkedin_account_sid: '<string>',
entity_urn: '<string>',
text: '<string>',
parent_comment_urn: '<string>',
mentions: [{profile_id: '<string>', name: '<string>'}],
client_reference: '<string>',
confirmed_not_sent: '<string>',
confirmed_sent: '<string>'
})
};
fetch('https://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment', 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://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment",
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([
'linkedin_account_sid' => '<string>',
'entity_urn' => '<string>',
'text' => '<string>',
'parent_comment_urn' => '<string>',
'mentions' => [
[
'profile_id' => '<string>',
'name' => '<string>'
]
],
'client_reference' => '<string>',
'confirmed_not_sent' => '<string>',
'confirmed_sent' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Team-SID: <api-key>"
],
]);
$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://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment"
payload := strings.NewReader("{\n \"linkedin_account_sid\": \"<string>\",\n \"entity_urn\": \"<string>\",\n \"text\": \"<string>\",\n \"parent_comment_urn\": \"<string>\",\n \"mentions\": [\n {\n \"profile_id\": \"<string>\",\n \"name\": \"<string>\"\n }\n ],\n \"client_reference\": \"<string>\",\n \"confirmed_not_sent\": \"<string>\",\n \"confirmed_sent\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Team-SID", "<api-key>")
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://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment")
.header("Authorization", "Bearer <token>")
.header("Team-SID", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"linkedin_account_sid\": \"<string>\",\n \"entity_urn\": \"<string>\",\n \"text\": \"<string>\",\n \"parent_comment_urn\": \"<string>\",\n \"mentions\": [\n {\n \"profile_id\": \"<string>\",\n \"name\": \"<string>\"\n }\n ],\n \"client_reference\": \"<string>\",\n \"confirmed_not_sent\": \"<string>\",\n \"confirmed_sent\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Team-SID"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"linkedin_account_sid\": \"<string>\",\n \"entity_urn\": \"<string>\",\n \"text\": \"<string>\",\n \"parent_comment_urn\": \"<string>\",\n \"mentions\": [\n {\n \"profile_id\": \"<string>\",\n \"name\": \"<string>\"\n }\n ],\n \"client_reference\": \"<string>\",\n \"confirmed_not_sent\": \"<string>\",\n \"confirmed_sent\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"operation": "action",
"action": "<string>",
"item": "<unknown>",
"result": {
"comment_urn": "<string>",
"activity_log": {},
"idempotent_replay": true,
"content_differs": true
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
},
"pacing": {
"linkedin_account_sid": "<string>",
"limit_type": "<string>",
"next_call_after": "<string>",
"remaining_today": 1
}
}{
"success": false,
"error": {
"code": "validation_failed",
"message": "<string>",
"recoverable": true,
"suggestion": "<string>",
"field_errors": {},
"blockers": [
{
"type": "<string>",
"severity": "hard",
"description": "<string>",
"entity_sid": "<string>",
"resolution": "<string>",
"resolution_hint": "<string>",
"count": 123
}
],
"context": {}
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
}
}{
"success": false,
"error": {
"code": "validation_failed",
"message": "<string>",
"recoverable": true,
"suggestion": "<string>",
"field_errors": {},
"blockers": [
{
"type": "<string>",
"severity": "hard",
"description": "<string>",
"entity_sid": "<string>",
"resolution": "<string>",
"resolution_hint": "<string>",
"count": 123
}
],
"context": {}
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
}
}Create LinkedIn comment
Leave ONE outbound comment on any LinkedIn post (wire create-comment): entity_urn takes a post urn in any family or a post URL carrying one, and the comment is written on the post’s social thread (see entity_urn). Outward and fire-on-success: the post does NOT need to be tracked or owned by us. Identity-bound: linkedin_account_sid REQUIRED, spends the comment_posts bucket (30/day at a 360 s floor), saturation returns 429. Reply to an existing comment via parent_comment_urn. mentions turns exact substrings of text into clickable profile links ( pairs; a name missing or out of order is a 422 with no dispatch spent). Returns the created comment ref plus the activity-log row. Sends once per client_reference and place (the post as LinkedIn files it, or the comment a reply answers): a repeat is answered, never sent twice; on a 409 read error.context.send_outcome, never resend under a new key. To react use react_linkedin_post; to resolve a post URL use get_activity_urn_by_url.
Contract:
- MCP tool
create_linkedin_comment, registry packagemcp.linkedin/linkedin_posting, mountlinkedin.content. - Operation
action, response envelopeaction. - Flags: dangerous: the MCP layer gates it behind a preview/commit token, and the effect cannot be undone through this API; mass action: this verb’s own request accepts a filter or targets[] set, which the owning service drains; step eligible: gtm.service.orchestration can run this verb as a mass-action plan step, once per item; paced: a call spends the
comment_postssmart-limit bucket of the account it runs on, and calls of one bucket are spaced per account. A short wait is slept by the server; a longer one answers 429rate_limitedwithcontext.retry_after, and the same call succeeds from that moment. Parallel calls on one account queue behind each other.
curl --request POST \
--url https://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Team-SID: <api-key>' \
--data '
{
"linkedin_account_sid": "<string>",
"entity_urn": "<string>",
"text": "<string>",
"parent_comment_urn": "<string>",
"mentions": [
{
"profile_id": "<string>",
"name": "<string>"
}
],
"client_reference": "<string>",
"confirmed_not_sent": "<string>",
"confirmed_sent": "<string>"
}
'import requests
url = "https://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment"
payload = {
"linkedin_account_sid": "<string>",
"entity_urn": "<string>",
"text": "<string>",
"parent_comment_urn": "<string>",
"mentions": [
{
"profile_id": "<string>",
"name": "<string>"
}
],
"client_reference": "<string>",
"confirmed_not_sent": "<string>",
"confirmed_sent": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Team-SID": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: 'Bearer <token>',
'Team-SID': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
linkedin_account_sid: '<string>',
entity_urn: '<string>',
text: '<string>',
parent_comment_urn: '<string>',
mentions: [{profile_id: '<string>', name: '<string>'}],
client_reference: '<string>',
confirmed_not_sent: '<string>',
confirmed_sent: '<string>'
})
};
fetch('https://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment', 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://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment",
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([
'linkedin_account_sid' => '<string>',
'entity_urn' => '<string>',
'text' => '<string>',
'parent_comment_urn' => '<string>',
'mentions' => [
[
'profile_id' => '<string>',
'name' => '<string>'
]
],
'client_reference' => '<string>',
'confirmed_not_sent' => '<string>',
'confirmed_sent' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Team-SID: <api-key>"
],
]);
$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://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment"
payload := strings.NewReader("{\n \"linkedin_account_sid\": \"<string>\",\n \"entity_urn\": \"<string>\",\n \"text\": \"<string>\",\n \"parent_comment_urn\": \"<string>\",\n \"mentions\": [\n {\n \"profile_id\": \"<string>\",\n \"name\": \"<string>\"\n }\n ],\n \"client_reference\": \"<string>\",\n \"confirmed_not_sent\": \"<string>\",\n \"confirmed_sent\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Team-SID", "<api-key>")
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://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment")
.header("Authorization", "Bearer <token>")
.header("Team-SID", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"linkedin_account_sid\": \"<string>\",\n \"entity_urn\": \"<string>\",\n \"text\": \"<string>\",\n \"parent_comment_urn\": \"<string>\",\n \"mentions\": [\n {\n \"profile_id\": \"<string>\",\n \"name\": \"<string>\"\n }\n ],\n \"client_reference\": \"<string>\",\n \"confirmed_not_sent\": \"<string>\",\n \"confirmed_sent\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.gtm-api.com/linkedin/v4/api/linkedin-posting/comment")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Team-SID"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"linkedin_account_sid\": \"<string>\",\n \"entity_urn\": \"<string>\",\n \"text\": \"<string>\",\n \"parent_comment_urn\": \"<string>\",\n \"mentions\": [\n {\n \"profile_id\": \"<string>\",\n \"name\": \"<string>\"\n }\n ],\n \"client_reference\": \"<string>\",\n \"confirmed_not_sent\": \"<string>\",\n \"confirmed_sent\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"operation": "action",
"action": "<string>",
"item": "<unknown>",
"result": {
"comment_urn": "<string>",
"activity_log": {},
"idempotent_replay": true,
"content_differs": true
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
},
"pacing": {
"linkedin_account_sid": "<string>",
"limit_type": "<string>",
"next_call_after": "<string>",
"remaining_today": 1
}
}{
"success": false,
"error": {
"code": "validation_failed",
"message": "<string>",
"recoverable": true,
"suggestion": "<string>",
"field_errors": {},
"blockers": [
{
"type": "<string>",
"severity": "hard",
"description": "<string>",
"entity_sid": "<string>",
"resolution": "<string>",
"resolution_hint": "<string>",
"count": 123
}
],
"context": {}
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
}
}{
"success": false,
"error": {
"code": "validation_failed",
"message": "<string>",
"recoverable": true,
"suggestion": "<string>",
"field_errors": {},
"blockers": [
{
"type": "<string>",
"severity": "hard",
"description": "<string>",
"entity_sid": "<string>",
"resolution": "<string>",
"resolution_hint": "<string>",
"count": 123
}
],
"context": {}
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
}
}Authorizations
Access token issued by gtm.service.id. Its access_identity claim carries team_sid, actor_sid and actor_type, and that team scope is authoritative.
Team scope for tokens that do not carry one. Ignored when the token already names a team.
Body
Request body of create_linkedin_comment.
LinkedIn account sid (ln_ac_…), the authoring account. Identity-bound: REQUIRED, posts publish AS this account.
18^ln_ac_The post to comment on. A comment is written on the post's social thread, and LinkedIn refuses one sent to any other key (live 2026-09-16: the activity urn of a ugcPost company post answered linkedin_400, its ugcPost urn took the comment), so a post handle is re-addressed at its thread before the wire. urn:li:ugcPost: and urn:li:groupPost:- are the thread and go as is. urn:li:activity: and urn:li:share: cost one post read first (the read enrich_linkedin_post_details makes, on this account, cached 7 days): a share post is commented at its activity urn, a ugcPost post at its ugcPost urn, a repost without commentary at the original. A post the read returns null for (deleted, or not visible to the account) is refused 422 post_not_resolvable. To reply under a comment, pass parent_comment_urn (no post read then); a comment urn given here is a reply under that comment too. A post URL works when the id is in it: the feed permalink, the /posts/-activity-- share link, the -ugcPost-- share link. A link WITHOUT an id (a shortlink, a bare slug URL) is refused 422 entity_urn_not_resolvable: call enrich_linkedin_get_activity_urn_by_url first. The post does NOT need to be tracked or owned by us. Renamed from activity_urn on 2026-07-30; the old name is no longer accepted.
1 - 512The comment body (caller-supplied, no templates or AI in-app).
1 - 1250Reply target: the comment URN to reply under. Omit or null for a top-level comment.
512Profile mentions (2026-08-21). Names are matched in text left to right in list order, each search starting after the previous mention. The node computes the character offsets itself.
Show child attributes
Show child attributes
Your key for this ONE comment (max 255, byte for byte): one key per comment, the same key on every repeat of it. The key and its place (the post as LinkedIn files it, named by any of its urns, or for a reply the comment it answers) are one comment whatever it says, and a repeat never goes out twice: it answers 200 with what the first send made (result.idempotent_replay, result.content_differs when this request says something else) or 409 naming error.context.send_outcome (in_flight, unknown with retry_after and send_decisive_at, or sent; not_sent with blocking_activity_log_sid when another comment in doubt holds the place). The same key at another place is another comment. Without a key the same words at the same place count as a repeat for an hour after they went out. check_linkedin_posting_sent asks by it.
255A person's word that an earlier attempt of THIS send is not on LinkedIn: its activity_log_sid (ln_al_...), from the 409 or check_linkedin_posting_sent, given only after someone looked on LinkedIn. Taken once the attempt can no longer land (before that: 409 send_outcome_unknown, waiting_for may_still_land, retry_after and send_decisive_at that moment): it settles that attempt not_sent and this request goes out. Moot when the attempt is no longer in doubt; another send's sid is 422 not_this_message.
18^ln_al_A person's word that an earlier attempt of THIS send IS on LinkedIn: its activity_log_sid, as for confirmed_not_sent (never both). Taken at once; it settles the attempt sent and sends nothing: the answer is 409 concurrent_send_in_flight with send_outcome sent while what it made is not known (LinkedIn is read for it), or 200 with it once a read has found it. Another send's sid is 422 not_this_message; an attempt already proved not sent is 409 confirmed_sent_contradicts.
18^ln_al_Response
action success envelope.
true action kebab-case verb; matches the route segment.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes