
Bankr Dev Api Basics
- 363 installs
- 80 repo stars
- Updated March 24, 2026
- bankrbot/claude-plugins
bankr dev - api basics is a Claude skill that documents Bankr Agent API authentication, endpoints, job polling, and TypeScript interfaces for developers who are integrating Bankr into a new backend or agent service.
About
bankr dev - api basics is a Bankr developer skill bundled in the bankr-agent-dev plugin for Claude Code and portable to Cursor via skills install. It explains how the Bankr Agent API uses an asynchronous submit-poll-complete pattern: POST `/agent/prompt` returns a job ID, GET `/agent/job/{jobId}` reports progress every ~2 seconds, and POST `/agent/job/{jobId}/cancel` stops long-running work. The skill covers `X-API-Key` authentication with `bk_...` keys, response formats, rich-data objects for swaps and NFT flows, and TypeScript interface references for typed clients. Developers reach for it when starting a trading bot, dashboard, or CLI that wraps Bankr before writing custom polling logic. Triggers include questions about Bankr API basics, job status handling, response formats, and how Bankr agent jobs complete.
- Auth and API key setup
- Core endpoint patterns
- Request and response schemas
- Error handling conventions
- Foundation for advanced Bankr skills
Bankr Dev Api Basics by the numbers
- 363 all-time installs (skills.sh)
- Ranked #1,130 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Jul 31, 2026 (Skillselion catalog sync)
npx skills add https://github.com/bankrbot/claude-plugins --skill bankr-dev---api-basicsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 363 |
|---|---|
| repo stars | ★ 80 |
| Last updated | March 24, 2026 |
| Repository | bankrbot/claude-plugins ↗ |
How does the Bankr Agent API async job pattern work?
Onboard developers to Bankr authentication, endpoints, request shapes, and error handling when integrating the platform into a new backend service or agent backend.
Who is it for?
Backend developers onboarding to Bankr Agent API who need endpoint docs and async job polling patterns before coding.
Skip if: Developers who only need one-off natural-language trades without building custom API clients or job handlers.
When should I use this skill?
User asks how Bankr API works, about job status, response formats, or building on Bankr Agent API.
What you get
Documented endpoint map, job lifecycle diagram, and TypeScript interface references for Bankr API consumers.
- Endpoint reference
- Job lifecycle documentation
- TypeScript interface guidance
By the numbers
- Documents 3 core Agent API endpoints: prompt, job status, and cancel
- Recommends ~2-second polling interval for job status checks
Files
Bankr Agent API Essentials
When answering questions about the Bankr API: 1. Explain the asynchronous job pattern (submit-poll-complete) 2. Reference the endpoint documentation and TypeScript examples below 3. For detailed response structures, consult references/job-response-schema.md 4. Point developers to working examples in examples/ directory
---
The Bankr Agent API enables programmatic access to crypto trading, market analysis, and prediction markets through a simple asynchronous job pattern.
Core Concept: Asynchronous Job Pattern
All Bankr operations follow a submit-poll-complete pattern:
1. Submit a natural language prompt to start a job 2. Poll the job status every 2 seconds 3. Receive results when status is terminal (completed/failed/cancelled)
This pattern handles operations that may take 30 seconds to 2+ minutes (trades, complex analysis).
API Endpoints
Base URL
https://api.bankr.botAuthentication
All requests require the x-api-key header:
x-api-key: bk_your_api_key_hereThe API key is tied to a specific user's Bankr account and wallet.
Endpoint 1: Submit Prompt
POST /agent/prompt
Content-Type: application/json
{
"prompt": "Buy $50 of ETH on Base"
}Response:
{
"success": true,
"jobId": "job_abc123",
"status": "pending"
}Code example:
async function submitPrompt(prompt: string): Promise<{ jobId: string }> {
const response = await fetch(`${API_URL}/agent/prompt`, {
method: "POST",
headers: {
"x-api-key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ prompt }),
});
const data = await response.json();
if (!data.success) throw new Error(data.error || "Failed to submit");
return { jobId: data.jobId };
}Endpoint 2: Get Job Status
GET /agent/job/{jobId}Response:
{
"success": true,
"jobId": "job_abc123",
"status": "completed",
"prompt": "What is the price of ETH?",
"response": "Ethereum (ETH) is currently trading at $3,245.67...",
"transactions": [],
"statusUpdates": [
{ "message": "Fetching price data...", "timestamp": "2024-01-15T10:00:02Z" }
],
"createdAt": "2024-01-15T10:00:00Z",
"completedAt": "2024-01-15T10:00:05Z",
"processingTime": 5000
}Code example:
async function getJobStatus(jobId: string): Promise<JobStatusResponse> {
const response = await fetch(`${API_URL}/agent/job/${jobId}`, {
headers: { "x-api-key": API_KEY },
});
return response.json();
}For complete TypeScript interfaces, see references/job-response-schema.md.
Endpoint 3: Cancel Job
POST /agent/job/{jobId}/cancel
Content-Type: application/jsonResponse:
{
"success": true,
"jobId": "job_abc123",
"status": "cancelled",
"prompt": "Buy $50 of ETH on Base",
"cancelledAt": "2024-01-15T10:00:15Z"
}When to cancel:
- User requests to stop a long-running operation
- Timeout exceeded and want to abort cleanly
- Detected an error condition that makes the job unnecessary
Code example:
async function cancelJob(jobId: string): Promise<JobStatusResponse> {
const response = await fetch(`${API_URL}/agent/job/${jobId}/cancel`, {
method: "POST",
headers: {
"x-api-key": API_KEY,
"Content-Type": "application/json",
},
});
return response.json();
}
// Usage: Cancel if job takes too long
const timeout = setTimeout(async () => {
console.log("Job taking too long, cancelling...");
await cancelJob(jobId);
}, 60000); // Cancel after 60 secondsJob Status States
| Status | Meaning | Action |
|---|---|---|
pending | Job queued, not started | Keep polling |
processing | Job running | Keep polling, check statusUpdates |
completed | Job finished successfully | Read response and transactions |
failed | Job encountered error | Check error field |
cancelled | Job was cancelled | No further action |
What You Can Do
The Bankr API accepts natural language prompts for:
Crypto Trading:
- "Buy $50 of ETH on Base"
- "Sell 100 USDC for SOL on Solana"
- "Swap 0.1 ETH for BNKR"
Price & Market Data:
- "What is the price of Bitcoin?"
- "Show me ETH price chart"
- "What are the top gainers today?"
Polymarket Predictions:
- "What are the odds on the next election?"
- "Bet $10 on the Eagles to win"
- "Show me trending prediction markets"
DeFi Operations:
- "What's the TVL on Aave?"
- "Show me best yields for USDC"
- "Check my portfolio balance"
Polling Best Practices
async function waitForCompletion(jobId: string): Promise<JobStatus> {
const POLL_INTERVAL = 2000; // 2 seconds
const MAX_POLLS = 120; // 4 minutes max
for (let i = 0; i < MAX_POLLS; i++) {
const status = await getJobStatus(jobId);
// Terminal states
if (['completed', 'failed', 'cancelled'].includes(status.status)) {
return status;
}
// Log progress updates
if (status.statusUpdates?.length) {
console.log('Progress:', status.statusUpdates.at(-1)?.message);
}
await new Promise(r => setTimeout(r, POLL_INTERVAL));
}
throw new Error('Job timed out');
}Key Response Fields
When a job completes, the response includes:
- `response`: The text answer from Bankr
- `transactions`: Array of executed transactions with chain/token details
- `richData`: Images or charts (base64 or URL)
- `statusUpdates`: Progress messages during execution
- `processingTime`: Duration in milliseconds
For complete field documentation, see references/job-response-schema.md.
Error Handling
Handle these error cases:
1. Missing API key: Check BANKR_API_KEY before making requests 2. HTTP errors: API returns 4xx/5xx with error text 3. Job failures: Check status === 'failed' and read error field 4. Timeouts: Implement max poll count (recommended: 4 minutes)
Example: Complete Flow
// 1. Submit prompt
const { jobId } = await submitPrompt("What is the price of ETH?");
// 2. Poll until complete
const result = await waitForCompletion(jobId);
// 3. Handle result
if (result.status === 'completed') {
console.log(result.response);
// "Ethereum (ETH) is currently trading at $3,245.67..."
} else if (result.status === 'failed') {
console.error('Error:', result.error);
}Additional Resources
Reference Files
- `references/job-response-schema.md` - Complete TypeScript interfaces and field documentation
Example Files
- `examples/basic-client.ts` - Simple API client implementation
- `examples/polling-with-updates.ts` - Polling with status update handling
/**
* Basic Bankr Agent API Client
*
* A minimal TypeScript client for the Bankr Agent API.
* Copy this into your project and customize as needed.
*/
const API_URL = process.env.BANKR_API_URL || "https://api.bankr.bot";
const API_KEY = process.env.BANKR_API_KEY;
// Types
interface PromptResponse {
success: boolean;
jobId?: string;
status?: string;
message?: string;
error?: string;
}
interface JobStatusResponse {
success: boolean;
jobId: string;
status: "pending" | "processing" | "completed" | "failed" | "cancelled";
prompt: string;
response?: string;
transactions?: Transaction[];
richData?: RichData[];
statusUpdates?: StatusUpdate[];
error?: string;
createdAt: string;
completedAt?: string;
processingTime?: number;
}
interface Transaction {
type: string;
metadata?: {
humanReadableMessage?: string;
inputTokenTicker?: string;
outputTokenTicker?: string;
inputTokenAmount?: string;
outputTokenAmount?: string;
transaction?: {
chainId: number;
to: string;
data: string;
gas?: string;
value?: string;
};
};
}
interface StatusUpdate {
message: string;
timestamp: string;
}
interface RichData {
type: string;
base64?: string;
url?: string;
}
// API Functions
/**
* Submit a prompt to the Bankr Agent API
*/
export async function submitPrompt(prompt: string): Promise<PromptResponse> {
if (!API_KEY) {
throw new Error("BANKR_API_KEY environment variable is not set");
}
const response = await fetch(`${API_URL}/agent/prompt`, {
method: "POST",
headers: {
"x-api-key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ prompt }),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API request failed: ${response.status} - ${errorText}`);
}
return response.json();
}
/**
* Get the status of a Bankr job
*/
export async function getJobStatus(jobId: string): Promise<JobStatusResponse> {
if (!API_KEY) {
throw new Error("BANKR_API_KEY environment variable is not set");
}
const response = await fetch(`${API_URL}/agent/job/${jobId}`, {
method: "GET",
headers: {
"x-api-key": API_KEY,
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API request failed: ${response.status} - ${errorText}`);
}
return response.json();
}
/**
* Cancel a running Bankr job
*/
export async function cancelJob(jobId: string): Promise<JobStatusResponse> {
if (!API_KEY) {
throw new Error("BANKR_API_KEY environment variable is not set");
}
const response = await fetch(`${API_URL}/agent/job/${jobId}/cancel`, {
method: "POST",
headers: {
"x-api-key": API_KEY,
"Content-Type": "application/json",
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API request failed: ${response.status} - ${errorText}`);
}
return response.json();
}
/**
* Wait for a job to complete, polling every 2 seconds
*/
export async function waitForCompletion(
jobId: string,
options?: {
pollInterval?: number;
maxPolls?: number;
onStatusUpdate?: (message: string) => void;
}
): Promise<JobStatusResponse> {
const pollInterval = options?.pollInterval ?? 2000;
const maxPolls = options?.maxPolls ?? 120; // 4 minutes default
let lastUpdateCount = 0;
for (let i = 0; i < maxPolls; i++) {
const status = await getJobStatus(jobId);
// Report new status updates
if (options?.onStatusUpdate && status.statusUpdates) {
for (let j = lastUpdateCount; j < status.statusUpdates.length; j++) {
options.onStatusUpdate(status.statusUpdates[j].message);
}
lastUpdateCount = status.statusUpdates.length;
}
// Check for terminal states
if (["completed", "failed", "cancelled"].includes(status.status)) {
return status;
}
await new Promise((resolve) => setTimeout(resolve, pollInterval));
}
throw new Error(`Job ${jobId} timed out after ${maxPolls * pollInterval}ms`);
}
// Usage Example
async function main() {
try {
// Submit a prompt
console.log("Submitting prompt...");
const { jobId } = await submitPrompt("What is the price of ETH?");
console.log(`Job submitted: ${jobId}`);
// Wait for completion with status updates
console.log("Waiting for completion...");
const result = await waitForCompletion(jobId, {
onStatusUpdate: (msg) => console.log(` > ${msg}`),
});
// Handle result
if (result.status === "completed") {
console.log("\nResult:");
console.log(result.response);
if (result.transactions?.length) {
console.log("\nTransactions:");
for (const tx of result.transactions) {
console.log(` - ${tx.metadata?.humanReadableMessage || tx.type}`);
}
}
} else if (result.status === "failed") {
console.error(`\nJob failed: ${result.error}`);
}
} catch (error) {
console.error("Error:", error);
}
}
// Run if executed directly
main();
/**
* Advanced Polling with Status Updates
*
* Demonstrates how to poll for job completion while streaming
* status updates to the user in real-time.
*/
import { submitPrompt, getJobStatus, JobStatusResponse } from "./basic-client";
interface PollingOptions {
pollInterval?: number; // ms between polls (default: 2000)
maxDuration?: number; // max total duration in ms (default: 240000 = 4 min)
onProgress?: (update: ProgressUpdate) => void;
}
interface ProgressUpdate {
type: "status" | "message" | "complete" | "error";
status?: string;
message?: string;
result?: JobStatusResponse;
}
/**
* Poll for job completion with progress callbacks
*/
export async function pollWithProgress(
jobId: string,
options: PollingOptions = {}
): Promise<JobStatusResponse> {
const pollInterval = options.pollInterval ?? 2000;
const maxDuration = options.maxDuration ?? 240000;
const startTime = Date.now();
let lastStatus = "";
let lastUpdateIndex = 0;
while (Date.now() - startTime < maxDuration) {
const job = await getJobStatus(jobId);
// Report status changes
if (job.status !== lastStatus) {
lastStatus = job.status;
options.onProgress?.({
type: "status",
status: job.status,
message: `Job status: ${job.status}`,
});
}
// Report new status updates
if (job.statusUpdates) {
for (let i = lastUpdateIndex; i < job.statusUpdates.length; i++) {
options.onProgress?.({
type: "message",
message: job.statusUpdates[i].message,
});
}
lastUpdateIndex = job.statusUpdates.length;
}
// Check for terminal states
if (job.status === "completed") {
options.onProgress?.({
type: "complete",
result: job,
message: "Job completed successfully",
});
return job;
}
if (job.status === "failed") {
options.onProgress?.({
type: "error",
result: job,
message: job.error || "Job failed",
});
return job;
}
if (job.status === "cancelled") {
options.onProgress?.({
type: "error",
result: job,
message: "Job was cancelled",
});
return job;
}
await new Promise((r) => setTimeout(r, pollInterval));
}
throw new Error(`Job timed out after ${maxDuration}ms`);
}
/**
* Execute a prompt and wait for result with progress updates
*/
export async function executeWithProgress(
prompt: string,
options: PollingOptions = {}
): Promise<JobStatusResponse> {
const { jobId } = await submitPrompt(prompt);
options.onProgress?.({
type: "status",
status: "submitted",
message: `Job submitted: ${jobId}`,
});
return pollWithProgress(jobId, options);
}
// Example: CLI with live progress output
async function cliExample() {
const prompt = process.argv[2] || "What is the price of Bitcoin?";
console.log(`\nExecuting: "${prompt}"\n`);
console.log("─".repeat(50));
const result = await executeWithProgress(prompt, {
onProgress: (update) => {
const timestamp = new Date().toLocaleTimeString();
switch (update.type) {
case "status":
console.log(`[${timestamp}] Status: ${update.status}`);
break;
case "message":
console.log(`[${timestamp}] > ${update.message}`);
break;
case "complete":
console.log(`[${timestamp}] ✓ Completed`);
break;
case "error":
console.log(`[${timestamp}] ✗ ${update.message}`);
break;
}
},
});
console.log("─".repeat(50));
if (result.status === "completed" && result.response) {
console.log("\nResponse:");
console.log(result.response);
if (result.transactions?.length) {
console.log("\nTransactions:");
for (const tx of result.transactions) {
const msg = tx.metadata?.humanReadableMessage || tx.type;
console.log(` • ${msg}`);
}
}
if (result.processingTime) {
console.log(`\nProcessing time: ${result.processingTime}ms`);
}
} else if (result.error) {
console.error(`\nError: ${result.error}`);
}
}
// Run CLI example if executed directly
cliExample().catch(console.error);
Bankr API Response Schema
Complete TypeScript interfaces for the Bankr Agent API responses.
Submit Prompt Response
interface PromptResponse {
success: boolean;
jobId?: string; // Present on success
status?: string; // Usually "pending"
message?: string; // Info message
error?: string; // Present on failure
}Job Status Response
interface JobStatusResponse {
success: boolean;
jobId: string;
status: "pending" | "processing" | "completed" | "failed" | "cancelled";
prompt: string; // Original prompt submitted
response?: string; // Final text response (on completion)
transactions?: Transaction[];// Executed blockchain transactions
richData?: RichData[]; // Images, charts, visualizations
statusUpdates?: StatusUpdate[]; // Progress messages
error?: string; // Error message (on failure)
createdAt: string; // ISO timestamp
completedAt?: string; // ISO timestamp (on completion)
startedAt?: string; // ISO timestamp (when processing began)
cancelledAt?: string; // ISO timestamp (if cancelled)
processingTime?: number; // Duration in milliseconds
}Transaction Structure
Transactions represent blockchain operations executed by Bankr:
interface Transaction {
type: string; // Transaction type identifier
metadata?: {
// Raw transaction data (for advanced use)
transaction?: {
chainId: number; // Chain ID (e.g., 8453 for Base)
to: string; // Contract address
data: string; // Encoded call data
gas?: string; // Gas limit
value?: string; // ETH value in wei
};
// Human-readable summary
humanReadableMessage?: string; // e.g., "Swapped 0.1 ETH for 150 USDC"
// Token details for swaps/trades
inputTokenTicker?: string; // e.g., "ETH"
outputTokenTicker?: string; // e.g., "USDC"
inputTokenAmount?: string; // e.g., "0.1"
outputTokenAmount?: string; // e.g., "150"
};
}Common Chain IDs
| Chain | ID |
|---|---|
| Ethereum Mainnet | 1 |
| Base | 8453 |
| Polygon | 137 |
| Arbitrum | 42161 |
| Optimism | 10 |
| Solana | - (different format) |
Status Update Structure
Status updates provide real-time progress during job execution:
interface StatusUpdate {
message: string; // Human-readable progress message
timestamp: string; // ISO timestamp
}Example status updates sequence:
[
{ "message": "Analyzing request...", "timestamp": "2024-01-15T10:00:01Z" },
{ "message": "Fetching current prices...", "timestamp": "2024-01-15T10:00:03Z" },
{ "message": "Preparing transaction...", "timestamp": "2024-01-15T10:00:05Z" },
{ "message": "Executing swap...", "timestamp": "2024-01-15T10:00:08Z" }
]Rich Data Structure
Rich data includes images, charts, and other media:
interface RichData {
type: string; // Content type (e.g., "image/png", "chart")
base64?: string; // Base64-encoded content
url?: string; // URL to content
}Usage:
- Check for
base64first for inline content - Fall back to
urlfor externally hosted content - The
typefield indicates how to render/display
Complete Example Response
{
"success": true,
"jobId": "job_abc123xyz",
"status": "completed",
"prompt": "Buy $50 of ETH on Base",
"response": "Successfully purchased 0.0154 ETH on Base for $50 USDC.",
"transactions": [
{
"type": "swap",
"metadata": {
"humanReadableMessage": "Swapped 50 USDC for 0.0154 ETH on Base",
"inputTokenTicker": "USDC",
"outputTokenTicker": "ETH",
"inputTokenAmount": "50",
"outputTokenAmount": "0.0154",
"transaction": {
"chainId": 8453,
"to": "0x...",
"data": "0x...",
"gas": "150000"
}
}
}
],
"statusUpdates": [
{ "message": "Analyzing request...", "timestamp": "2024-01-15T10:00:01Z" },
{ "message": "Fetching best route...", "timestamp": "2024-01-15T10:00:03Z" },
{ "message": "Executing swap...", "timestamp": "2024-01-15T10:00:08Z" }
],
"richData": [],
"createdAt": "2024-01-15T10:00:00Z",
"startedAt": "2024-01-15T10:00:01Z",
"completedAt": "2024-01-15T10:00:12Z",
"processingTime": 12000
}Error Response Example
{
"success": true,
"jobId": "job_def456",
"status": "failed",
"prompt": "Buy $1000000 of ETH",
"error": "Insufficient balance. Available: $500 USDC",
"statusUpdates": [
{ "message": "Analyzing request...", "timestamp": "2024-01-15T10:00:01Z" },
{ "message": "Checking balance...", "timestamp": "2024-01-15T10:00:03Z" }
],
"createdAt": "2024-01-15T10:00:00Z",
"completedAt": "2024-01-15T10:00:05Z",
"processingTime": 5000
}Related skills
How it compares
Use bankr dev api basics for endpoint and job lifecycle docs; switch to bankr dev client patterns when you need reusable bankr-client.ts scaffolding.
FAQ
What is the Bankr Agent API job pattern?
Bankr Agent API uses submit-poll-complete: POST `/agent/prompt` returns a job ID, poll GET `/agent/job/{jobId}` about every 2 seconds, then read results or cancel via POST `/agent/job/{jobId}/cancel`.
How do you authenticate Bankr API requests?
Bankr dev api basics documents `X-API-Key` headers with `bk_...` keys. Agent endpoints require `agentApiEnabled` on the key before `/agent/*` routes accept prompts.