curl --request PATCH \
--url https://productlane.com/api/v2/threads/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"external_ids": [
"<string>"
],
"text": "<string>",
"title": "<string>",
"assignee_id": "<string>",
"contact_id": "<string>",
"company_id": "<string>",
"tag_ids": [
"<string>"
],
"project_id": "<string>",
"snoozed_until": "2023-11-07T05:31:56Z",
"closed_loop": true,
"ai_draft_html": "<string>",
"ai_draft_sources": "<array>",
"clear_ai_draft_error": true,
"notify": {
"slack": true,
"email": true
}
}
'import requests
url = "https://productlane.com/api/v2/threads/{id}"
payload = {
"external_ids": ["<string>"],
"text": "<string>",
"title": "<string>",
"assignee_id": "<string>",
"contact_id": "<string>",
"company_id": "<string>",
"tag_ids": ["<string>"],
"project_id": "<string>",
"snoozed_until": "2023-11-07T05:31:56Z",
"closed_loop": True,
"ai_draft_html": "<string>",
"ai_draft_sources": "<array>",
"clear_ai_draft_error": True,
"notify": {
"slack": True,
"email": True
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
external_ids: ['<string>'],
text: '<string>',
title: '<string>',
assignee_id: '<string>',
contact_id: '<string>',
company_id: '<string>',
tag_ids: ['<string>'],
project_id: '<string>',
snoozed_until: '2023-11-07T05:31:56Z',
closed_loop: true,
ai_draft_html: '<string>',
ai_draft_sources: '<array>',
clear_ai_draft_error: true,
notify: {slack: true, email: true}
})
};
fetch('https://productlane.com/api/v2/threads/{id}', 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://productlane.com/api/v2/threads/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'external_ids' => [
'<string>'
],
'text' => '<string>',
'title' => '<string>',
'assignee_id' => '<string>',
'contact_id' => '<string>',
'company_id' => '<string>',
'tag_ids' => [
'<string>'
],
'project_id' => '<string>',
'snoozed_until' => '2023-11-07T05:31:56Z',
'closed_loop' => true,
'ai_draft_html' => '<string>',
'ai_draft_sources' => '<array>',
'clear_ai_draft_error' => true,
'notify' => [
'slack' => true,
'email' => true
]
]),
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://productlane.com/api/v2/threads/{id}"
payload := strings.NewReader("{\n \"external_ids\": [\n \"<string>\"\n ],\n \"text\": \"<string>\",\n \"title\": \"<string>\",\n \"assignee_id\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"company_id\": \"<string>\",\n \"tag_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"snoozed_until\": \"2023-11-07T05:31:56Z\",\n \"closed_loop\": true,\n \"ai_draft_html\": \"<string>\",\n \"ai_draft_sources\": \"<array>\",\n \"clear_ai_draft_error\": true,\n \"notify\": {\n \"slack\": true,\n \"email\": true\n }\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://productlane.com/api/v2/threads/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"external_ids\": [\n \"<string>\"\n ],\n \"text\": \"<string>\",\n \"title\": \"<string>\",\n \"assignee_id\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"company_id\": \"<string>\",\n \"tag_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"snoozed_until\": \"2023-11-07T05:31:56Z\",\n \"closed_loop\": true,\n \"ai_draft_html\": \"<string>\",\n \"ai_draft_sources\": \"<array>\",\n \"clear_ai_draft_error\": true,\n \"notify\": {\n \"slack\": true,\n \"email\": true\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://productlane.com/api/v2/threads/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"external_ids\": [\n \"<string>\"\n ],\n \"text\": \"<string>\",\n \"title\": \"<string>\",\n \"assignee_id\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"company_id\": \"<string>\",\n \"tag_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"snoozed_until\": \"2023-11-07T05:31:56Z\",\n \"closed_loop\": true,\n \"ai_draft_html\": \"<string>\",\n \"ai_draft_sources\": \"<array>\",\n \"clear_ai_draft_error\": true,\n \"notify\": {\n \"slack\": true,\n \"email\": true\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"created_at": "<string>",
"updated_at": "<string>",
"title": "<string>",
"text": "<string>",
"pain_level": "UNKNOWN",
"origin": "<string>",
"custom_channel": "<string>",
"status": "open",
"tab": "open",
"snoozed_until": "<string>",
"last_inbound_message_at": "<string>",
"last_outbound_message_at": "<string>",
"last_state_change_at": "<string>",
"is_read": true,
"contact": {
"id": "<string>",
"email": "<string>",
"name": "<string>",
"image_url": "<string>",
"company_id": "<string>",
"is_subscribed": true,
"external_ids": [
"<string>"
],
"integration_ids": {
"intercom": "<string>",
"front": "<string>",
"zendesk": "<string>",
"hubspot": "<string>",
"plain": "<string>",
"productboard": "<string>",
"slack_channel": "<string>"
},
"created_at": "<string>",
"updated_at": "<string>"
},
"company": {
"id": "<string>",
"name": "<string>",
"logo_url": "<string>",
"domains": [
"<string>"
],
"size": 123,
"revenue": 123,
"status_id": "<string>",
"status_name": "<string>",
"tier_id": "<string>",
"tier_name": "<string>",
"owner": {
"id": "<string>",
"name": "<string>",
"avatar_url": "<string>"
},
"external_ids": [
"<string>"
],
"integration_ids": {
"intercom": "<string>",
"front": "<string>",
"zendesk": "<string>",
"hubspot": "<string>",
"plain": "<string>",
"productboard": "<string>",
"slack_channel": "<string>"
},
"created_at": "<string>",
"updated_at": "<string>"
},
"assignee": {
"id": "<string>",
"name": "<string>",
"email": "<string>",
"image_url": "<string>"
},
"reporter": {
"id": "<string>",
"name": "<string>",
"email": "<string>",
"image_url": "<string>"
},
"tags": [
{
"id": "<string>",
"name": "<string>",
"color": "<string>",
"icon": "<string>",
"tag_group_id": "<string>",
"created_at": "<string>",
"updated_at": "<string>"
}
],
"customer_needs": [
{
"id": "<string>",
"thread_id": "<string>",
"project_id": "<string>",
"issue_id": "<string>",
"priority": 123,
"created_at": "<string>",
"updated_at": "<string>"
}
],
"external_ids": [
"<string>"
],
"integration_ids": {
"intercom": "<string>",
"front": "<string>",
"zendesk": "<string>",
"hubspot": "<string>",
"plain": "<string>",
"productboard": "<string>",
"slack_channel": "<string>"
},
"ai_draft_html": "<string>",
"ai_draft_sources": "<array>",
"ai_draft_generated_at": "<string>",
"ai_draft_started_at": "<string>",
"ai_draft_error_at": "<string>"
}{
"message": "<string>",
"code": "<string>",
"issues": [
{
"message": "<string>"
}
]
}Update thread
Update a thread. To patch many threads, call /threads/bulk-update once instead of looping here. Requires the threads:write scope.
curl --request PATCH \
--url https://productlane.com/api/v2/threads/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"external_ids": [
"<string>"
],
"text": "<string>",
"title": "<string>",
"assignee_id": "<string>",
"contact_id": "<string>",
"company_id": "<string>",
"tag_ids": [
"<string>"
],
"project_id": "<string>",
"snoozed_until": "2023-11-07T05:31:56Z",
"closed_loop": true,
"ai_draft_html": "<string>",
"ai_draft_sources": "<array>",
"clear_ai_draft_error": true,
"notify": {
"slack": true,
"email": true
}
}
'import requests
url = "https://productlane.com/api/v2/threads/{id}"
payload = {
"external_ids": ["<string>"],
"text": "<string>",
"title": "<string>",
"assignee_id": "<string>",
"contact_id": "<string>",
"company_id": "<string>",
"tag_ids": ["<string>"],
"project_id": "<string>",
"snoozed_until": "2023-11-07T05:31:56Z",
"closed_loop": True,
"ai_draft_html": "<string>",
"ai_draft_sources": "<array>",
"clear_ai_draft_error": True,
"notify": {
"slack": True,
"email": True
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
external_ids: ['<string>'],
text: '<string>',
title: '<string>',
assignee_id: '<string>',
contact_id: '<string>',
company_id: '<string>',
tag_ids: ['<string>'],
project_id: '<string>',
snoozed_until: '2023-11-07T05:31:56Z',
closed_loop: true,
ai_draft_html: '<string>',
ai_draft_sources: '<array>',
clear_ai_draft_error: true,
notify: {slack: true, email: true}
})
};
fetch('https://productlane.com/api/v2/threads/{id}', 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://productlane.com/api/v2/threads/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'external_ids' => [
'<string>'
],
'text' => '<string>',
'title' => '<string>',
'assignee_id' => '<string>',
'contact_id' => '<string>',
'company_id' => '<string>',
'tag_ids' => [
'<string>'
],
'project_id' => '<string>',
'snoozed_until' => '2023-11-07T05:31:56Z',
'closed_loop' => true,
'ai_draft_html' => '<string>',
'ai_draft_sources' => '<array>',
'clear_ai_draft_error' => true,
'notify' => [
'slack' => true,
'email' => true
]
]),
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://productlane.com/api/v2/threads/{id}"
payload := strings.NewReader("{\n \"external_ids\": [\n \"<string>\"\n ],\n \"text\": \"<string>\",\n \"title\": \"<string>\",\n \"assignee_id\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"company_id\": \"<string>\",\n \"tag_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"snoozed_until\": \"2023-11-07T05:31:56Z\",\n \"closed_loop\": true,\n \"ai_draft_html\": \"<string>\",\n \"ai_draft_sources\": \"<array>\",\n \"clear_ai_draft_error\": true,\n \"notify\": {\n \"slack\": true,\n \"email\": true\n }\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://productlane.com/api/v2/threads/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"external_ids\": [\n \"<string>\"\n ],\n \"text\": \"<string>\",\n \"title\": \"<string>\",\n \"assignee_id\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"company_id\": \"<string>\",\n \"tag_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"snoozed_until\": \"2023-11-07T05:31:56Z\",\n \"closed_loop\": true,\n \"ai_draft_html\": \"<string>\",\n \"ai_draft_sources\": \"<array>\",\n \"clear_ai_draft_error\": true,\n \"notify\": {\n \"slack\": true,\n \"email\": true\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://productlane.com/api/v2/threads/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"external_ids\": [\n \"<string>\"\n ],\n \"text\": \"<string>\",\n \"title\": \"<string>\",\n \"assignee_id\": \"<string>\",\n \"contact_id\": \"<string>\",\n \"company_id\": \"<string>\",\n \"tag_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"snoozed_until\": \"2023-11-07T05:31:56Z\",\n \"closed_loop\": true,\n \"ai_draft_html\": \"<string>\",\n \"ai_draft_sources\": \"<array>\",\n \"clear_ai_draft_error\": true,\n \"notify\": {\n \"slack\": true,\n \"email\": true\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"created_at": "<string>",
"updated_at": "<string>",
"title": "<string>",
"text": "<string>",
"pain_level": "UNKNOWN",
"origin": "<string>",
"custom_channel": "<string>",
"status": "open",
"tab": "open",
"snoozed_until": "<string>",
"last_inbound_message_at": "<string>",
"last_outbound_message_at": "<string>",
"last_state_change_at": "<string>",
"is_read": true,
"contact": {
"id": "<string>",
"email": "<string>",
"name": "<string>",
"image_url": "<string>",
"company_id": "<string>",
"is_subscribed": true,
"external_ids": [
"<string>"
],
"integration_ids": {
"intercom": "<string>",
"front": "<string>",
"zendesk": "<string>",
"hubspot": "<string>",
"plain": "<string>",
"productboard": "<string>",
"slack_channel": "<string>"
},
"created_at": "<string>",
"updated_at": "<string>"
},
"company": {
"id": "<string>",
"name": "<string>",
"logo_url": "<string>",
"domains": [
"<string>"
],
"size": 123,
"revenue": 123,
"status_id": "<string>",
"status_name": "<string>",
"tier_id": "<string>",
"tier_name": "<string>",
"owner": {
"id": "<string>",
"name": "<string>",
"avatar_url": "<string>"
},
"external_ids": [
"<string>"
],
"integration_ids": {
"intercom": "<string>",
"front": "<string>",
"zendesk": "<string>",
"hubspot": "<string>",
"plain": "<string>",
"productboard": "<string>",
"slack_channel": "<string>"
},
"created_at": "<string>",
"updated_at": "<string>"
},
"assignee": {
"id": "<string>",
"name": "<string>",
"email": "<string>",
"image_url": "<string>"
},
"reporter": {
"id": "<string>",
"name": "<string>",
"email": "<string>",
"image_url": "<string>"
},
"tags": [
{
"id": "<string>",
"name": "<string>",
"color": "<string>",
"icon": "<string>",
"tag_group_id": "<string>",
"created_at": "<string>",
"updated_at": "<string>"
}
],
"customer_needs": [
{
"id": "<string>",
"thread_id": "<string>",
"project_id": "<string>",
"issue_id": "<string>",
"priority": 123,
"created_at": "<string>",
"updated_at": "<string>"
}
],
"external_ids": [
"<string>"
],
"integration_ids": {
"intercom": "<string>",
"front": "<string>",
"zendesk": "<string>",
"hubspot": "<string>",
"plain": "<string>",
"productboard": "<string>",
"slack_channel": "<string>"
},
"ai_draft_html": "<string>",
"ai_draft_sources": "<array>",
"ai_draft_generated_at": "<string>",
"ai_draft_started_at": "<string>",
"ai_draft_error_at": "<string>"
}{
"message": "<string>",
"code": "<string>",
"issues": [
{
"message": "<string>"
}
]
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Body
Caller-owned identifiers. Replaces the full set.
1512UNKNOWN, LOW, MEDIUM, HIGH Workspace user id. Pass null to unassign.
Pass null to detach. Also propagates onto the thread's contact.
Replaces the thread's full tag set.
When snoozed, snoozed_until is required.
open, snoozed, done With status=done, marks the thread as fully closed.
HTML body prefilled into the composer for the next outbound reply. Setting this stamps ai_draft_generated_at and clears ai_draft_started_at + ai_draft_error_at. Pass null (or an empty string) to clear the draft.
Sources the AI consulted while generating the draft. Surfaces in the composer's "Drafted by AI" tooltip. Shape: { type, title, identifier?, url? }[]. Pass null to clear.
When true, clears ai_draft_error_at. Use this to dismiss a stuck error pill in the composer without writing a new draft.
Side-channel notifications to fire alongside the write (Slack and/or email).
Show child attributes
Show child attributes
Response
Successful response
UNKNOWN, LOW, MEDIUM, HIGH Channel name when origin is custom, such as whatsapp.
open, snoozed, done open, new, needs-response, my, snoozed, done Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Caller-owned cross-system identifiers (CRM ids, etc.). Replaced wholesale on create/update.
Show child attributes
Show child attributes