Record feedback for the organization that owns the credential. Requires an API key with the feedback.write scope. Agents and MCP servers should post to the organization’s feedback URL instead.
curl --request POST \
--url https://api.usenotra.com/v1/feedback \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"message": "The search tool times out when the query has quotes.",
"title": "Search times out on quoted queries",
"kind": "bug",
"sentiment": "negative",
"source": "mcp",
"projectId": "<string>",
"agentClient": "claude-code",
"agentModel": "claude-opus-5",
"toolVersion": "1.2.0",
"userAgent": "<string>",
"contextUrl": "https://docs.example.com/api/search",
"externalId": "<string>",
"idempotencyKey": "<string>",
"metadata": {}
}
'import requests
url = "https://api.usenotra.com/v1/feedback"
payload = {
"message": "The search tool times out when the query has quotes.",
"title": "Search times out on quoted queries",
"kind": "bug",
"sentiment": "negative",
"source": "mcp",
"projectId": "<string>",
"agentClient": "claude-code",
"agentModel": "claude-opus-5",
"toolVersion": "1.2.0",
"userAgent": "<string>",
"contextUrl": "https://docs.example.com/api/search",
"externalId": "<string>",
"idempotencyKey": "<string>",
"metadata": {}
}
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({
message: 'The search tool times out when the query has quotes.',
title: 'Search times out on quoted queries',
kind: 'bug',
sentiment: 'negative',
source: 'mcp',
projectId: '<string>',
agentClient: 'claude-code',
agentModel: 'claude-opus-5',
toolVersion: '1.2.0',
userAgent: '<string>',
contextUrl: 'https://docs.example.com/api/search',
externalId: '<string>',
idempotencyKey: '<string>',
metadata: {}
})
};
fetch('https://api.usenotra.com/v1/feedback', 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.usenotra.com/v1/feedback",
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([
'message' => 'The search tool times out when the query has quotes.',
'title' => 'Search times out on quoted queries',
'kind' => 'bug',
'sentiment' => 'negative',
'source' => 'mcp',
'projectId' => '<string>',
'agentClient' => 'claude-code',
'agentModel' => 'claude-opus-5',
'toolVersion' => '1.2.0',
'userAgent' => '<string>',
'contextUrl' => 'https://docs.example.com/api/search',
'externalId' => '<string>',
'idempotencyKey' => '<string>',
'metadata' => [
]
]),
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.usenotra.com/v1/feedback"
payload := strings.NewReader("{\n \"message\": \"The search tool times out when the query has quotes.\",\n \"title\": \"Search times out on quoted queries\",\n \"kind\": \"bug\",\n \"sentiment\": \"negative\",\n \"source\": \"mcp\",\n \"projectId\": \"<string>\",\n \"agentClient\": \"claude-code\",\n \"agentModel\": \"claude-opus-5\",\n \"toolVersion\": \"1.2.0\",\n \"userAgent\": \"<string>\",\n \"contextUrl\": \"https://docs.example.com/api/search\",\n \"externalId\": \"<string>\",\n \"idempotencyKey\": \"<string>\",\n \"metadata\": {}\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.usenotra.com/v1/feedback")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"message\": \"The search tool times out when the query has quotes.\",\n \"title\": \"Search times out on quoted queries\",\n \"kind\": \"bug\",\n \"sentiment\": \"negative\",\n \"source\": \"mcp\",\n \"projectId\": \"<string>\",\n \"agentClient\": \"claude-code\",\n \"agentModel\": \"claude-opus-5\",\n \"toolVersion\": \"1.2.0\",\n \"userAgent\": \"<string>\",\n \"contextUrl\": \"https://docs.example.com/api/search\",\n \"externalId\": \"<string>\",\n \"idempotencyKey\": \"<string>\",\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.usenotra.com/v1/feedback")
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 \"message\": \"The search tool times out when the query has quotes.\",\n \"title\": \"Search times out on quoted queries\",\n \"kind\": \"bug\",\n \"sentiment\": \"negative\",\n \"source\": \"mcp\",\n \"projectId\": \"<string>\",\n \"agentClient\": \"claude-code\",\n \"agentModel\": \"claude-opus-5\",\n \"toolVersion\": \"1.2.0\",\n \"userAgent\": \"<string>\",\n \"contextUrl\": \"https://docs.example.com/api/search\",\n \"externalId\": \"<string>\",\n \"idempotencyKey\": \"<string>\",\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_body{
"feedback": {
"id": "<string>",
"projectId": "<string>",
"source": "mcp",
"kind": "bug",
"sentiment": "negative",
"status": "new",
"title": "<string>",
"message": "<string>",
"agentClient": "<string>",
"agentModel": "<string>",
"toolVersion": "<string>",
"userAgent": "<string>",
"contextUrl": "<string>",
"externalId": "<string>",
"idempotencyKey": "<string>",
"metadata": {},
"resolvedAt": "<string>",
"createdAt": "<string>",
"updatedAt": "<string>"
},
"deduplicated": true
}{
"error": "<string>",
"code": "<string>",
"recovery": "<string>"
}{
"error": "<string>",
"code": "<string>",
"recovery": "<string>"
}{
"error": "<string>",
"code": "<string>",
"recovery": "<string>"
}{
"error": "<string>",
"code": "<string>",
"recovery": "<string>"
}{
"error": "<string>",
"limit": 2,
"remaining": 1,
"reset": 123
}{
"error": "<string>",
"code": "<string>",
"recovery": "<string>"
}Authorizations
Send your API key in the Authorization header as Bearer API_KEY.
Body
The feedback itself.
1 - 4000"The search tool times out when the query has quotes."
Short summary. When omitted, Notra writes one.
1 - 200"Search times out on quoted queries"
What kind of feedback this is. When omitted, Notra classifies it.
bug, feature, praise, question, other "bug"
Overall sentiment. When omitted, Notra classifies it from the message.
negative, neutral, positive "negative"
Channel the feedback arrived through.
mcp, api, sdk "mcp"
Project to file the feedback under.
1^[A-Za-z0-9_-]{1,100}$The agent or client that submitted the feedback.
1 - 200"claude-code"
1 - 200"claude-opus-5"
1 - 200"1.2.0"
1 - 2048Page or resource the feedback is about.
2048"https://docs.example.com/api/search"
Your own identifier for the user or session.
1 - 200Submitting the same key twice returns the original feedback instead of creating a duplicate.
1 - 200Arbitrary JSON attached to the feedback, up to 8 KB.
Show child attributes
Show child attributes
Was this page helpful?
curl --request POST \
--url https://api.usenotra.com/v1/feedback \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"message": "The search tool times out when the query has quotes.",
"title": "Search times out on quoted queries",
"kind": "bug",
"sentiment": "negative",
"source": "mcp",
"projectId": "<string>",
"agentClient": "claude-code",
"agentModel": "claude-opus-5",
"toolVersion": "1.2.0",
"userAgent": "<string>",
"contextUrl": "https://docs.example.com/api/search",
"externalId": "<string>",
"idempotencyKey": "<string>",
"metadata": {}
}
'import requests
url = "https://api.usenotra.com/v1/feedback"
payload = {
"message": "The search tool times out when the query has quotes.",
"title": "Search times out on quoted queries",
"kind": "bug",
"sentiment": "negative",
"source": "mcp",
"projectId": "<string>",
"agentClient": "claude-code",
"agentModel": "claude-opus-5",
"toolVersion": "1.2.0",
"userAgent": "<string>",
"contextUrl": "https://docs.example.com/api/search",
"externalId": "<string>",
"idempotencyKey": "<string>",
"metadata": {}
}
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({
message: 'The search tool times out when the query has quotes.',
title: 'Search times out on quoted queries',
kind: 'bug',
sentiment: 'negative',
source: 'mcp',
projectId: '<string>',
agentClient: 'claude-code',
agentModel: 'claude-opus-5',
toolVersion: '1.2.0',
userAgent: '<string>',
contextUrl: 'https://docs.example.com/api/search',
externalId: '<string>',
idempotencyKey: '<string>',
metadata: {}
})
};
fetch('https://api.usenotra.com/v1/feedback', 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.usenotra.com/v1/feedback",
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([
'message' => 'The search tool times out when the query has quotes.',
'title' => 'Search times out on quoted queries',
'kind' => 'bug',
'sentiment' => 'negative',
'source' => 'mcp',
'projectId' => '<string>',
'agentClient' => 'claude-code',
'agentModel' => 'claude-opus-5',
'toolVersion' => '1.2.0',
'userAgent' => '<string>',
'contextUrl' => 'https://docs.example.com/api/search',
'externalId' => '<string>',
'idempotencyKey' => '<string>',
'metadata' => [
]
]),
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.usenotra.com/v1/feedback"
payload := strings.NewReader("{\n \"message\": \"The search tool times out when the query has quotes.\",\n \"title\": \"Search times out on quoted queries\",\n \"kind\": \"bug\",\n \"sentiment\": \"negative\",\n \"source\": \"mcp\",\n \"projectId\": \"<string>\",\n \"agentClient\": \"claude-code\",\n \"agentModel\": \"claude-opus-5\",\n \"toolVersion\": \"1.2.0\",\n \"userAgent\": \"<string>\",\n \"contextUrl\": \"https://docs.example.com/api/search\",\n \"externalId\": \"<string>\",\n \"idempotencyKey\": \"<string>\",\n \"metadata\": {}\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.usenotra.com/v1/feedback")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"message\": \"The search tool times out when the query has quotes.\",\n \"title\": \"Search times out on quoted queries\",\n \"kind\": \"bug\",\n \"sentiment\": \"negative\",\n \"source\": \"mcp\",\n \"projectId\": \"<string>\",\n \"agentClient\": \"claude-code\",\n \"agentModel\": \"claude-opus-5\",\n \"toolVersion\": \"1.2.0\",\n \"userAgent\": \"<string>\",\n \"contextUrl\": \"https://docs.example.com/api/search\",\n \"externalId\": \"<string>\",\n \"idempotencyKey\": \"<string>\",\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.usenotra.com/v1/feedback")
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 \"message\": \"The search tool times out when the query has quotes.\",\n \"title\": \"Search times out on quoted queries\",\n \"kind\": \"bug\",\n \"sentiment\": \"negative\",\n \"source\": \"mcp\",\n \"projectId\": \"<string>\",\n \"agentClient\": \"claude-code\",\n \"agentModel\": \"claude-opus-5\",\n \"toolVersion\": \"1.2.0\",\n \"userAgent\": \"<string>\",\n \"contextUrl\": \"https://docs.example.com/api/search\",\n \"externalId\": \"<string>\",\n \"idempotencyKey\": \"<string>\",\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_body{
"feedback": {
"id": "<string>",
"projectId": "<string>",
"source": "mcp",
"kind": "bug",
"sentiment": "negative",
"status": "new",
"title": "<string>",
"message": "<string>",
"agentClient": "<string>",
"agentModel": "<string>",
"toolVersion": "<string>",
"userAgent": "<string>",
"contextUrl": "<string>",
"externalId": "<string>",
"idempotencyKey": "<string>",
"metadata": {},
"resolvedAt": "<string>",
"createdAt": "<string>",
"updatedAt": "<string>"
},
"deduplicated": true
}{
"error": "<string>",
"code": "<string>",
"recovery": "<string>"
}{
"error": "<string>",
"code": "<string>",
"recovery": "<string>"
}{
"error": "<string>",
"code": "<string>",
"recovery": "<string>"
}{
"error": "<string>",
"code": "<string>",
"recovery": "<string>"
}{
"error": "<string>",
"limit": 2,
"remaining": 1,
"reset": 123
}{
"error": "<string>",
"code": "<string>",
"recovery": "<string>"
}