A discussion is one support conversation: a title, the customer's first message and every reply after it. With the API you can list discussions the way your dashboard does, read one with its comments, start one for a customer, and resolve, move, queue or delete it.
Replies have their own article: Comments.
Listing discussions
GET /yoursite/discussions
GET /yoursite/discussions/{state}
curl -H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
https://api.tenderapp.com/yoursite/discussions/open
{
"offset": 0,
"total": 8,
"per_page": 30,
"discussions": [
{
"title": "Mobile app crashes when I open an attachment",
"number": 6,
"state": "open",
"public": false,
"unread": true,
"unresponded": true,
"comments_count": 1,
"last_updated_at": "2026-10-09T06:05:12Z",
"author_name": "Jonas Berg",
"author_email": "jonas.berg@mailbox.example.org",
"href": "https://api.tenderapp.com/yoursite/discussions/13",
"category_href": "https://api.tenderapp.com/yoursite/categories/8",
"html_href": "https://yoursite.tenderapp.com/discussions/problems/6"
}
]
}
Each entry has all the discussion fields except comments (trimmed here). Lists come 30 to a page, the most recently active first; ask for the next with ?page=2.
Without a state you get every discussion that isn't deleted or spam. The states are:
| State | Discussions |
|---|---|
new |
Open, and no supporter has replied yet. |
open |
Waiting on someone, with no ticket linked. |
assigned |
Open, with a ticket linked in an issue tracker. |
resolved |
Resolved. |
pending |
Open or assigned, and waiting on you: the customer wrote last, or someone marked it unread. This is your dashboard's inbox. |
deleted |
Deleted, and spam too. |
spam |
Marked as spam. |
Narrowing a list
These parameters work on every list:
| Parameter | |
|---|---|
page |
The page to return, from 1. |
since |
A number: only discussions whose id is higher. A date or time (2026-10-01, 2026-10-09T06:00:00Z): only discussions with activity after it. |
sort |
updated makes a date in since compare with updated_at (any change to the discussion, including staff actions) instead of last_updated_at. |
public |
public=true: only public discussions. |
user_email |
Only discussions started by this email address, with or without an account. |
user_id |
Only discussions started by this user. |
user_current |
user_current=true: only discussions you started. |
For example, every discussion with activity since the start of October:
curl -H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
"https://api.tenderapp.com/yoursite/discussions?since=2026-10-01"
And a customer's discussions:
curl -H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
"https://api.tenderapp.com/yoursite/discussions?user_email=lucas@northwind.example.com"
The pending list
/discussions/pending (alone, or in a category or queue) is built from the same live counts as your dashboard, so it's fast, but it works a little differently:
- It has no
offset,totalorper_page, and its pages are longer than 30. Ask for the next page until one comes back with no discussions. sortorders it:new(most recently active first, the default) orold(longest waiting first).- Other parameters, such as
user_emailandpublic, are ignored. Addingsinceoruser_idgives you an ordinary list of pending discussions instead, with paging fields.
curl -H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
"https://api.tenderapp.com/yoursite/discussions/pending?sort=old"
{
"discussions": [
{
"title": "Recurring tasks skip a week after the clocks changed",
"state": "assigned",
"unread": true,
"last_updated_at": "2026-10-07T05:47:11Z",
"href": "https://api.tenderapp.com/yoursite/discussions/7"
},
{
"title": "Google Calendar sync stopped working",
"state": "open",
"unread": true,
"last_updated_at": "2026-10-08T09:47:12Z",
"href": "https://api.tenderapp.com/yoursite/discussions/10"
}
]
}
In a category, a queue or for a user
The same lists, with the same states and parameters, exist under a category, a queue and a user:
GET /yoursite/categories/{category}/discussions/{state}
GET /yoursite/queues/{queue_id}/discussions/{state}
GET /yoursite/users/{user_id}/discussions/{state}
{category} is the category's id or its permalink (problems). The state is optional everywhere.
curl -H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
https://api.tenderapp.com/yoursite/categories/questions/discussions/resolved
For queues:
- Without a state, a queue lists its open and assigned discussions. Ask for
/resolved(or another state) to see the rest. mineis your own queue: the discussions assigned to you.
curl -H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
https://api.tenderapp.com/yoursite/queues/mine/discussions
noneis the discussions in no queue at all. It only works with the pending list:/queues/none/discussions/pending.
More about queues in Queues.
Response time statistics
GET /yoursite/discussions/statistics
How quickly your team first answers customers, over a day, week, month or year:
curl -H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
"https://api.tenderapp.com/yoursite/discussions/statistics?date_range=week"
{
"count": 9,
"percent": 69.0,
"average": 0,
"stddev": 0.31,
"median": 0
}
| Parameter | |
|---|---|
date_range |
day, week (the default), month or year. |
end |
A date in the period you want, such as 2026-09-15. The default is today. |
| Field | |
|---|---|
count |
Discussions started in the period that have had a first reply from a supporter. |
percent |
That count as a percentage of all discussions started in the period. |
average |
The average time to that first reply, in seconds. |
median |
The median time to the first reply, in seconds. |
stddev |
The standard deviation of those times, in seconds. |
A period with no replied discussions gives an empty object, {}.
A single discussion
GET /yoursite/discussions/{id}
GET /yoursite/categories/{category}/discussions/{number}
A discussion has an id, which is unique on your site, and a number, which counts up within its category: the 6 in yoursite.tenderapp.com/discussions/problems/6. Either way you get the discussion and its comments:
curl -H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
https://api.tenderapp.com/yoursite/discussions/17
curl -H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
https://api.tenderapp.com/yoursite/categories/problems/discussions/8
{
"title": "Board view is slow with 500+ cards",
"permalink": "board-view-is-slow-with-500-cards",
"number": 8,
"state": "open",
"public": false,
"unread": true,
"unresponded": false,
"hidden": false,
"via": "web",
"last_via": "web",
"comments_count": 3,
"watchers_count": 2,
"avg_response_time": 0,
"company_id": 4,
"cached_queue_list": [8],
"created_at": "2026-10-08T10:47:13Z",
"updated_at": "2026-10-09T06:47:24Z",
"last_updated_at": "2026-10-08T14:07:13Z",
"author_name": "Fatima Haddad",
"author_email": "fatima@harborpine.example.com",
"last_author_name": "Fatima Haddad",
"last_author_email": "fatima@harborpine.example.com",
"comments": [
{
"body": "Our \"Fleet maintenance\" board has about 600 cards and takes 15 seconds to load. Other boards are fine.",
"number": 1,
"user_is_supporter": false,
"internal": false,
"author_name": "Fatima Haddad",
"created_at": "2026-10-08T10:47:13Z"
},
{
"body": "Discussion was added to Bugs queue",
"number": 2,
"user_is_supporter": true,
"internal": true,
"system_message": true,
"author_name": "Maya Okafor",
"created_at": "2026-10-08T10:48:13Z"
},
{
"body": "Thanks Fatima. Could you tell us which browser you're using, and whether filtering the board to one column makes it faster?",
"number": 3,
"user_is_supporter": true,
"internal": false,
"author_name": "Daniel Reyes",
"created_at": "2026-10-08T11:47:13Z"
}
],
"href": "https://api.tenderapp.com/yoursite/discussions/17",
"comments_href": "https://api.tenderapp.com/yoursite/discussions/17/comments{?page}",
"user_href": "https://api.tenderapp.com/yoursite/users/17",
"category_href": "https://api.tenderapp.com/yoursite/categories/8",
"resolve_href": "https://api.tenderapp.com/yoursite/discussions/17/resolve",
"queue_href": "https://api.tenderapp.com/yoursite/discussions/17/queue?queue={queue_id}",
"html_href": "https://yoursite.tenderapp.com/discussions/problems/8"
}
Comments come 30 to a page, oldest first, internal notes included; ?page=2 gives the next 30. A page past the last has no comments field. The comment fields are in Comments.
Deleted and spam discussions answer 404 here; find them with the deleted and spam lists.
Discussion fields
| Field | |
|---|---|
title |
The title. |
permalink |
The title as it appears in the discussion's address. |
number |
The discussion's number in its category. |
state |
open, assigned, resolved or spam. |
public |
true if anyone can read it in your forum; false if only the customer and your staff can. |
unread |
true while it waits on your team (it's in the pending list). |
unresponded |
true until a supporter replies (it's in the new list). |
hidden |
true when it has been deleted or marked as spam. |
via |
How it started: web (the website and the API), email and so on. |
last_via |
How the latest comment came in. |
comments_count |
Comments the customer can see, the first message included. Internal notes don't count. |
watchers_count |
People who get email about it. |
avg_response_time |
The average time your team took to reply, in seconds, or null before the first reply. |
company_id |
The author's company, or null. |
cached_queue_list |
The ids of the queues it's in. |
extras |
The extra information sent when it was created; only there when there is some. |
private_body |
The same extra information as an HTML list, as staff see it in Tender, or null. |
suggested_faqs |
Ids of KB articles Tender suggested to the customer while they wrote; only there when there are some. |
anon_user_token |
When the author has no account, the key in the link that lets them open the discussion; otherwise null. |
redirection_id |
Set when the discussion was moved; otherwise null. |
spawned_from_comment_id |
Set when it was split off from a comment in another discussion; otherwise null. |
activity_filter_stamp |
Used by your activity filters. |
last_comment_id, last_user_id |
The latest comment the customer can see, and its author. |
created_at |
When it was started. |
updated_at |
When anything about it last changed. |
last_updated_at |
When its latest comment was posted. |
author_name, author_email |
Who started it, with or without an account. |
last_author_name, last_author_email |
Who wrote the latest comment the customer can see. |
comments |
Its comments (a single discussion only). |
href |
This discussion. The last part is its id. |
comments_href |
Its comments. |
user_href |
The author, when they have an account. |
category_href |
Its category. |
resolve_href, unresolve_href, toggle_href, acknowledge_href, unacknowledge_href, change_category_href, queue_href, unqueue_href, restore_href |
The actions. |
html_href |
The discussion in Tender. |
Creating a discussion
POST /yoursite/categories/{category_id}/discussions
Use this to open a discussion for a customer, from a contact form or another system. {category_id} must be the category's id here (not its permalink). Send a JSON object:
curl -X POST \
-H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Export to CSV leaves out custom fields",
"body": "When I export a project to CSV, the custom field columns are empty.",
"author_email": "priya.nair@example.com",
"author_name": "Priya Nair",
"private": true,
"skip_spam": true,
"extras": {"plan": "Business", "account_id": "4821"}}' \
https://api.tenderapp.com/yoursite/categories/8/discussions
The answer is 201 Created with the new discussion and its first comment:
{
"title": "Export to CSV leaves out custom fields",
"number": 10,
"state": "open",
"public": false,
"unread": true,
"unresponded": true,
"private_body": "<ul><li>Plan: Business</li><li>Account: 4821</li></ul>",
"extras": {
"plan": "Business",
"account_id": "4821"
},
"author_name": "Priya Nair",
"author_email": "priya.nair@example.com",
"comments": [
{
"body": "When I export a project to CSV, the custom field columns are empty.",
"number": 1,
"user_is_supporter": false,
"internal": false,
"author_name": "Priya Nair",
"author_email": "priya.nair@example.com",
"created_at": "2026-10-09T07:16:48Z"
}
],
"href": "https://api.tenderapp.com/yoursite/discussions/21",
"html_href": "https://yoursite.tenderapp.com/discussions/problems/10"
}
| Field | |
|---|---|
body |
Required. The first message. |
title |
The title. Without one it's "No subject". |
author_email |
Post it as this customer. Someone with an account on your site gets it under their account; anyone else is a guest, who gets replies by email. Without author_email (or unique_id), the discussion is yours. |
author_name |
The customer's name, with author_email. |
unique_id |
Post it as one of your single sign-on users: their id in your own system. If nobody has that id yet, a user is made from author_email, author_name and trusted; if someone has, their name and email are updated from those. A user that isn't valid gets 422. |
trusted |
With unique_id: true makes the user trusted, so their posts skip the spam check. |
public, private |
"public": true or "private": true. Discussions in private categories, and on sites without a public forum, are always private, and categories set to make discussions public do so for customers. |
extras |
Extra information about the customer or their account, as a JSON object of names and values (or that object as a string). See below. |
skip_spam |
true: don't run the spam check on it. |
skip_notifications |
true: don't email anyone about it. Only works when you post it yourself (without author_email). |
user_ip, user_agent, referrer |
The customer's IP address, browser and the page they came from, when you post from your own web form. The spam check uses them. |
Extra information
extras is for what your team needs to know about the customer: their plan, account number, app version. Tender shows it to staff above the discussion and never to the customer, and the API gives it back in extras and, as HTML, in private_body. Names are shown made readable: account_id is shown as "Account".
Errors
A field that isn't valid gets 422 with the problems:
{"body": ["can't be blank"]}
A missing or unknown category, or one given by its permalink, gets 422 with [["category_id", "can't be blank"]].
Attachments
To attach files, send the same fields as a multipart/form-data form instead of JSON, with each file as uploaded_assets[] (and 1 or 0 for true and false). See Comments.
Actions
Each action is a POST to the discussion's address and needs no body. They need a token with Read and write access.
| Action | What it does | Answer |
|---|---|---|
resolve |
Resolves it and adds an internal note saying you did. | The discussion |
unresolve |
Opens it again, with a note. | The discussion |
toggle |
Switches it between public and private, with a note. | The discussion |
acknowledge |
Takes it out of the pending list without replying. | The discussion |
unacknowledge |
Puts it back in the pending list, as if the customer had just written. | The discussion |
queue?queue={queue_id} |
Adds it to a queue, with a note. mine is your own queue, so this assigns it to you. |
{} |
unqueue?queue={queue_id} |
Takes it out of a queue, with a note. | {} |
change_category?to={category} |
Moves it to another category (by id or permalink). | {} |
restore |
Brings back a deleted or spam discussion. | {} |
The discussion that comes back has no comments in it. Resolving a resolved discussion, or opening an open one, changes nothing.
curl -X POST \
-H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
https://api.tenderapp.com/yoursite/discussions/21/resolve
curl -X POST \
-H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
"https://api.tenderapp.com/yoursite/discussions/21/queue?queue=10"
queue and unqueue without a queue, and change_category without to, get 422 with no body; an unknown queue or category gets 404.
Moving to another category
change_category gives the discussion a new id and a new number in its new category, and the old id stops working. To find it again, list the new category's discussions: it has the same title, author and comments, and an internal note saying where it came from.
Restoring
restore brings a deleted or spam discussion back, comments included, and tells the spam filter it was wrong when it was spam. A discussion that isn't deleted or spam gets 422:
{"error": "Discussion 22 isn't spam or deleted."}
Deleting
DELETE /yoursite/discussions/{id}
DELETE /yoursite/discussions/{id}?type=spam
DELETE /yoursite/discussions/{id}?type=harsh
curl -X DELETE \
-H "Accept: application/vnd.tender-v1+json" \
-H "Authorization: Bearer $TENDER_TOKEN" \
https://api.tenderapp.com/yoursite/discussions/22
The answer is {}. What happens depends on type:
type |
What it does |
|---|---|
none, or delete |
Deletes it: it moves to the deleted list, and you can restore it. |
spam |
Marks it as spam, the same as the Spam button in Tender: it moves to the spam list, the spam filter learns from it, and its author is blocked from posting again. Use it only for certain spam. You can restore it. |
harsh |
Deletes it and permanently erases its first message. Restoring it later brings back the discussion without that message. |
Tender finishes deleting in the background, so the lists can take a moment to show the change. A discussion that's already deleted or spam answers 404.
