From bankr-agent-dev
Implements Bankr Agent API async job workflows: submit prompts, poll status every 2s, handle transitions like pending/processing/completed, process rich data/responses, manage threads.
npx claudepluginhub bankrbot/claude-plugins --plugin bankr-agent-devThis skill uses the workspace's default tool permissions.
Complete reference for the asynchronous job pattern used by the Bankr Agent API.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Complete reference for the asynchronous job pattern used by the Bankr Agent API.
1. SUBMIT -> POST /agent/prompt -> Get jobId + threadId
2. POLL -> GET /agent/job/{id} -> Check status every 2s
3. COMPLETE -> Terminal status -> Process response + richData
const response = await fetch(`${API_URL}/agent/prompt`, {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
prompt: "What is my ETH balance?",
threadId: "thr_XYZ789", // optional: continue conversation
}),
});
// → { success: true, jobId: "job_abc123", threadId: "thr_XYZ789", status: "pending" }
Request fields:
prompt (string, required): Natural language prompt (max 10,000 chars)threadId (string, optional): Continue existing conversation. Omit for new thread.const status = await fetch(`${API_URL}/agent/job/${jobId}`, {
headers: { "x-api-key": API_KEY },
});
const cancel = await fetch(`${API_URL}/agent/job/${jobId}/cancel`, {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
});
| Status | Description | Action |
|---|---|---|
pending | Job queued, not started | Keep polling |
processing | Job running | Keep polling, show statusUpdates |
completed | Finished successfully | Read response and richData |
failed | Encountered error | Check error field |
cancelled | Was cancelled | No further action |
success, jobId, threadId, status, prompt, createdAtresponse — Natural language textrichData — Array of structured data (charts, social cards)transactions — Array of executed transactionscompletedAt, processingTimestatusUpdates — Array of { message, timestamp }startedAt, cancellableerror — Error messagecompletedAtcancelledAtasync function waitForCompletion(
jobId: string,
onProgress?: (message: string) => void
): Promise<JobStatusResponse> {
const POLL_INTERVAL = 2000; // 2 seconds
const MAX_POLLS = 150; // 5 minutes max
let lastUpdateCount = 0;
for (let i = 0; i < MAX_POLLS; i++) {
const status = await getJobStatus(jobId);
// Report new status updates
if (onProgress && status.statusUpdates) {
for (let j = lastUpdateCount; j < status.statusUpdates.length; j++) {
onProgress(status.statusUpdates[j].message);
}
lastUpdateCount = status.statusUpdates.length;
}
// Terminal states
if (["completed", "failed", "cancelled"].includes(status.status)) {
return status;
}
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
}
throw new Error("Job timed out");
}
// Start a conversation
const first = await submitPrompt("What is the price of ETH?");
const threadId = first.threadId;
// Continue the conversation (agent remembers context)
const second = await submitPrompt("And what about BTC?", threadId);
// Each response includes the same threadId
Completed jobs may include richData:
type RichData = {
type?: string; // "social-card", "chart", etc.
[key: string]: unknown;
};
The response field always has a text summary regardless of richData content.
| Status | Error | Resolution |
|---|---|---|
| 400 | Invalid request / Prompt too long | Check input (max 10,000 chars) |
| 401 | Authentication required | Check API key |
| 403 | Agent API not enabled | Enable at bankr.bot/api |
| 404 | Job not found | Check jobId is correct |
| 429 | Rate limit exceeded | Wait for resetAt timestamp |
// Handle rate limits
if (response.status === 429) {
const error = await response.json();
const waitMs = error.resetAt - Date.now();
console.log(`Rate limited. Resets in ${Math.ceil(waitMs / 60000)} minutes`);
}
import { submitPrompt, waitForCompletion } from "./bankr-client";
async function main() {
// Submit
const { jobId } = await submitPrompt("Swap 0.1 ETH for USDC on Base");
console.log(`Job: ${jobId}`);
// Poll with progress
const result = await waitForCompletion(jobId, (msg) => {
console.log(`Progress: ${msg}`);
});
// Handle result
if (result.status === "completed") {
console.log(result.response);
for (const tx of result.transactions || []) {
console.log(`Transaction: ${tx.type}`);
}
} else if (result.status === "failed") {
console.error(`Failed: ${result.error}`);
}
}
bankr-api-basics - Endpoint documentation and TypeScript interfacesbankr-client-patterns - Reusable client code with execute() helperbankr-sign-submit-api - Synchronous endpoints (no polling needed)