Build a bug-report bot
This walkthrough connects the API endpoints into one automation. A moderator approves a bug report in your community chat or support tool, and your bot files it as a card on the right board. The same steps work with any chat app, form, or ticket system that can send a message to your bot.
The bot needs an API key with the read permission to load board context and the create permission to file cards. Store the key in your bot's secret storage, never in source code.
The flow
- Find the board with
GET /api/v1/boards. Do this once and cache the board ID. - Fetch context with
GET /api/v1/boards/:boardIdto get the board'slistsandlabels. Pass them to your AI model. - Create the card with
POST /api/v1/cardsusing thelistId,title,description,priority, andlabelIdsthat the model chose.
Node.js example
This example uses the built-in fetch available in Node.js 18 and later. It has no SDK dependency.
const BASE = "https://projavo.com/api/v1";
const KEY = process.env.PROJAVO_API_KEY;
const headers = {
Authorization: `Bearer ${KEY}`,
"Content-Type": "application/json",
};
async function api(path, init = {}) {
const res = await fetch(`${BASE}${path}`, { ...init, headers });
const body = await res.json();
if (!res.ok) {
throw new Error(`${res.status} ${body.error?.code}: ${body.error?.message}`);
}
return body;
}
// 1. Board context for the model
const boardId = "<board-id>";
const { lists, labels } = await api(`/boards/${boardId}`);
// 2. Your AI model picks the list and labels, and writes the summary.
// Pass `lists` and `labels` in the prompt. It returns the JSON shown below.
const draft = await summarizeReportWithModel(reportMessages, { lists, labels });
// -> { listId, title, description, priority, labelIds }
// 3. Create the card. The list determines the board, so no boardId is sent.
const { card } = await api("/cards", {
method: "POST",
body: JSON.stringify(draft),
});
console.log(`Created card ${card.id}: ${card.title}`);
summarizeReportWithModel is a placeholder for your own code. Call whichever AI model provider you use, and make sure it returns the JSON shape described in the next section.
Ask your AI model for JSON
Give the model the lists and labels from step 2, and ask it to return a JSON object that matches the POST /api/v1/cards body. Here is an example prompt:
You are triaging a bug report from a community chat or support tool.
Choose the best list and labels from the options below, then summarize
the report as a card.
Lists: [{ "id": "...", "name": "Testing" }, ...]
Labels: [{ "id": "...", "name": "Bug" }, ...]
Respond with only JSON in this shape:
{
"listId": "one id from Lists",
"title": "short summary, 255 characters or fewer",
"description": "Markdown summary of the report",
"priority": "NONE | LOW | MEDIUM | HIGH | URGENT",
"labelIds": ["ids chosen only from Labels"]
}
A valid model response looks like this:
{
"listId": "<list-id>",
"title": "Vehicle spawns underground near Legion",
"description": "**Reported by:** a community member, approved by a moderator.\n\nWhen spawning the Adder near the fountain, the vehicle clips through the map.\n\n**Repro:** spawn the Adder near the fountain.",
"priority": "HIGH",
"labelIds": ["<bug-label-id>"]
}
Validate the model output before you send it. Check that listId is in the lists you fetched, that every labelIds item is in labels, and that priority is one of the five values above. The API rejects labels from other projects with 422 invalid_label.
Handle errors
The API returns errors as JSON with an error.code and an error.message. See Errors for the full list. Common cases for this flow:
| Status | Code | What to do |
|---|---|---|
401 | invalid_key | Check that the key is correct and has not been revoked. |
403 | insufficient_scope | The key is missing the read or create permission. Edit its permissions. |
404 | not_found | The list or board ID is wrong, or the key cannot access it. Refresh the cached IDs. |
422 | invalid_label | The model chose a label that is not in the board's project. Remove it or retry. |
429 | RATE_LIMITED | Wait a minute, then retry. The limit is 60 requests per minute per key. |
Keep the bot's request rate low. Cache the board ID and the lists and labels output, and refresh them only when your board structure changes.