Skip to content
Syncline

Slack API reference

<p>These are the Syncline endpoints for the Slack connector. They run on the Syncline API, take a Syncline key, and answer with the Syncline response shape. This page does not document Slack's own Web API, and nothing here should be read as a description of it: for the vendor routes, read Slack's developer documentation directly.</p><p>The connector holds the workspace authorisation, so your code never handles a Slack token. You name a channel by ID or by name, we resolve it, respect the per channel pacing Slack expects, and return the message timestamp you will need later to edit or thread onto that message.</p>
Base URL: https://api.syncline.topclb.com

Authentication

Send a Syncline secret key as a bearer token: Authorization: Bearer sk_live_... . The connection_id parameter selects which authorised Slack workspace to act in. Syncline posts as its own app with the name and icon you configured, because Slack does not allow a third party app to impersonate a member and we would not build that if it did.

Endpoints

<article class="sy-endpoint"><h3><code>POST /v1/connectors/slack/messages</code></h3><p>Post a message to a channel or as a threaded reply.</p><div class="sy-table-wrap"><table><thead><tr><th>Param</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>connection_id</td><td>string</td><td>yes</td><td>The authorised Slack workspace to post into.</td></tr><tr><td>channel</td><td>string</td><td>yes</td><td>Channel ID or name. Private channels work once the Syncline app has been invited to them.</td></tr><tr><td>text</td><td>string</td><td>yes</td><td>Message body in Slack markup. Bold, links and bullet lists are all supported.</td></tr><tr><td>thread_ts</td><td>string</td><td>no</td><td>Parent message timestamp. Supply it to reply in a thread instead of posting to the channel.</td></tr><tr><td>idempotency_key</td><td>string</td><td>no</td><td>Repeating a key within 24 hours returns the original message instead of posting twice.</td></tr></tbody></table></div><pre><code>{ &quot;run_id&quot;: &quot;run_1a77c930&quot;, &quot;connector&quot;: &quot;slack&quot;, &quot;status&quot;: &quot;ok&quot;, &quot;data&quot;: { &quot;message_ts&quot;: &quot;1757236921.004300&quot;, &quot;channel_id&quot;: &quot;C08H2QK4T&quot;, &quot;permalink&quot;: &quot;https://example.slack.com/archives/C08H2QK4T/p1757236921004300&quot;, &quot;posted_at&quot;: &quot;2026-09-07T10:02:01Z&quot; } }</code></pre></article><article class="sy-endpoint"><h3><code>PATCH /v1/connectors/slack/messages/{message_ts}</code></h3><p>Edit a message Syncline posted earlier, so a status line stays current.</p><div class="sy-table-wrap"><table><thead><tr><th>Param</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>connection_id</td><td>string</td><td>yes</td><td>The authorised Slack workspace holding the message.</td></tr><tr><td>channel</td><td>string</td><td>yes</td><td>The channel the message lives in. A timestamp is unique only within a channel.</td></tr><tr><td>text</td><td>string</td><td>yes</td><td>The replacement body. Slack shows an edited marker to readers.</td></tr></tbody></table></div><pre><code>{ &quot;run_id&quot;: &quot;run_2b91d044&quot;, &quot;connector&quot;: &quot;slack&quot;, &quot;status&quot;: &quot;ok&quot;, &quot;data&quot;: { &quot;message_ts&quot;: &quot;1757236921.004300&quot;, &quot;channel_id&quot;: &quot;C08H2QK4T&quot;, &quot;edited_at&quot;: &quot;2026-09-07T10:19:33Z&quot; } }</code></pre></article><article class="sy-endpoint"><h3><code>GET /v1/connectors/slack/channels</code></h3><p>List the channels this connection can post into.</p><div class="sy-table-wrap"><table><thead><tr><th>Param</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>connection_id</td><td>string</td><td>yes</td><td>The authorised Slack workspace to enumerate.</td></tr><tr><td>types</td><td>string</td><td>no</td><td>Comma separated list of public, private and im. Defaults to public,private.</td></tr><tr><td>limit</td><td>integer</td><td>no</td><td>Results per page, 1 to 200. Defaults to 100.</td></tr><tr><td>cursor</td><td>string</td><td>no</td><td>Opaque cursor from the previous response.</td></tr></tbody></table></div><pre><code>{ &quot;run_id&quot;: &quot;run_3c05ae71&quot;, &quot;connector&quot;: &quot;slack&quot;, &quot;status&quot;: &quot;ok&quot;, &quot;data&quot;: [ { &quot;channel_id&quot;: &quot;C08H2QK4T&quot;, &quot;name&quot;: &quot;revenue-alerts&quot;, &quot;is_private&quot;: true, &quot;member_count&quot;: 9 } ], &quot;next_cursor&quot;: null }</code></pre></article><article class="sy-endpoint"><h3><code>POST /v1/connectors/slack/reactions</code></h3><p>Add an emoji reaction as a quiet acknowledgement that a flow handled a message.</p><div class="sy-table-wrap"><table><thead><tr><th>Param</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>connection_id</td><td>string</td><td>yes</td><td>The authorised Slack workspace to act in.</td></tr><tr><td>channel</td><td>string</td><td>yes</td><td>The channel containing the target message.</td></tr><tr><td>message_ts</td><td>string</td><td>yes</td><td>The message timestamp to react to.</td></tr><tr><td>emoji</td><td>string</td><td>yes</td><td>Emoji short name without colons, such as white_check_mark.</td></tr></tbody></table></div><pre><code>{ &quot;run_id&quot;: &quot;run_4d16bf82&quot;, &quot;connector&quot;: &quot;slack&quot;, &quot;status&quot;: &quot;ok&quot;, &quot;data&quot;: { &quot;message_ts&quot;: &quot;1757236921.004300&quot;, &quot;emoji&quot;: &quot;white_check_mark&quot;, &quot;already_present&quot;: false } }</code></pre></article><article class="sy-endpoint"><h3><code>GET /v1/connectors/slack/events</code></h3><p>Read inbound message events the connector observed on your selected channels.</p><div class="sy-table-wrap"><table><thead><tr><th>Param</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>connection_id</td><td>string</td><td>yes</td><td>The authorised Slack workspace whose events you want.</td></tr><tr><td>channel</td><td>string</td><td>no</td><td>Restrict to one channel. Omit for every channel the connection watches.</td></tr><tr><td>since</td><td>string</td><td>no</td><td>ISO 8601 timestamp. Events are retained for 30 days on paid plans.</td></tr><tr><td>limit</td><td>integer</td><td>no</td><td>Results per page, 1 to 200. Defaults to 50.</td></tr></tbody></table></div><pre><code>{ &quot;run_id&quot;: &quot;run_5e27ca93&quot;, &quot;connector&quot;: &quot;slack&quot;, &quot;status&quot;: &quot;ok&quot;, &quot;data&quot;: [ { &quot;event_id&quot;: &quot;evt_7d31b0&quot;, &quot;type&quot;: &quot;message.keyword&quot;, &quot;channel_id&quot;: &quot;C08H2QK4T&quot;, &quot;author&quot;: &quot;U04QN7RM2&quot;, &quot;text&quot;: &quot;refund requested for order 20481&quot;, &quot;matched_keyword&quot;: &quot;refund&quot;, &quot;observed_at&quot;: &quot;2026-09-07T08:55:12Z&quot; } ], &quot;next_cursor&quot;: null }</code></pre></article>

Errors

<div class="sy-table-wrap"><table><thead><tr><th>Code</th><th>Meaning</th><th>What to do</th></tr></thead><tbody><tr><td>401 connection_revoked</td><td>The Slack app was removed from the workspace, or an admin revoked the install.</td><td>Reinstall Syncline from Connections. Queued messages are delivered on the next attempt rather than discarded.</td></tr><tr><td>403 not_in_channel</td><td>The channel is private and the Syncline app has not been invited to it.</td><td>Type /invite @Syncline in that channel, then retry. No scope change is needed.</td></tr><tr><td>404 channel_archived</td><td>The destination channel was archived, so nothing can be posted there.</td><td>Point the flow at a live channel, or unarchive the old one. Failed runs can be replayed once the destination exists.</td></tr><tr><td>422 message_too_long</td><td>The body exceeded the 4000 character limit Slack accepts for a single message.</td><td>Shorten the template, or split the content across a message and a threaded reply using thread_ts.</td></tr><tr><td>429 upstream_rate_limited</td><td>Slack throttled posting to this channel, typically around one message per second for apps.</td><td>Batch the notifications into a digest, or add a filter so fewer records reach the action at all.</td></tr></tbody></table></div>

Rate limits

Syncline allows 120 requests per minute per API key. Slack accepts roughly one message per second per channel for apps, so the connector spaces posts out and queues the overflow rather than dropping it. A queued call returns 202 with a queue_position, which is different from a failure and should not be retried by your code.

Code samples

<h4>curl</h4><pre><code>curl -X POST https://api.syncline.topclb.com/v1/connectors/slack/messages \ -H &quot;Authorization: Bearer $SYNCLINE_KEY&quot; \ -H &quot;Content-Type: application/json&quot; \ -d '{ &quot;connection_id&quot;: &quot;con_slack_4b8&quot;, &quot;channel&quot;: &quot;revenue-alerts&quot;, &quot;text&quot;: &quot;*Payment failed* 49.00 USD for lena@harbourline.example&quot; }'</code></pre><h4>node</h4><pre><code>const res = await fetch( &quot;https://api.syncline.topclb.com/v1/connectors/slack/messages&quot;, { method: &quot;POST&quot;, headers: { Authorization: `Bearer ${process.env.SYNCLINE_KEY}`, &quot;Content-Type&quot;: &quot;application/json&quot; }, body: JSON.stringify({ connection_id: &quot;con_slack_4b8&quot;, channel: &quot;revenue-alerts&quot;, text: &quot;*Payment failed* 49.00 USD&quot; }) } ); const { data } = await res.json(); console.log(data.permalink);</code></pre><h4>python</h4><pre><code>import os import requests resp = requests.post( &quot;https://api.syncline.topclb.com/v1/connectors/slack/messages&quot;, headers={&quot;Authorization&quot;: f&quot;Bearer {os.environ['SYNCLINE_KEY']}&quot;}, json={ &quot;connection_id&quot;: &quot;con_slack_4b8&quot;, &quot;channel&quot;: &quot;revenue-alerts&quot;, &quot;text&quot;: &quot;*Payment failed* 49.00 USD&quot;, }, timeout=30, ) resp.raise_for_status() print(resp.json()[&quot;data&quot;][&quot;message_ts&quot;])</code></pre>