GET
/
api
/
v2
/
users
/
jobs
curl --request GET \
  --url https://api.buena.ai/api/v2/users/jobs \
  --header 'x-api-key: <api-key>'
{
  "userId": "65e0a32640a597f6a7f897ea",
  "success": true,
  "data": {
    "jobs": [
      {
        "id": "job_123abc",
        "type": "linkedin_action",
        "status": "completed",
        "createdAt": "2024-01-20T10:00:00Z",
        "completedAt": "2024-01-20T10:02:00Z",
        "message": "Connection request sent successfully",
        "progress": 100,
        "metadata": {
          "actionType": "sendConnectionRequest",
          "profileUrl": "https://linkedin.com/in/johndoe",
          "delay": 75000
        }
      },
      {
        "id": "job_456def",
        "type": "data_enrichment",
        "status": "running",
        "createdAt": "2024-01-20T11:00:00Z",
        "completedAt": null,
        "message": "Processing lead enrichment batch",
        "progress": 60,
        "metadata": {
          "batchSize": 50,
          "processed": 30
        }
      }
    ],
    "statistics": {
      "total": 25,
      "pending": 3,
      "running": 2,
      "completed": 18,
      "failed": 2,
      "successRate": 85.7
    }
  }
}

Get User Jobs

Retrieve job status information, counts, and statistics for the authenticated user. This simplified endpoint automatically detects the user from the API key, eliminating the need for user IDs in the URL.

Requires a user-specific API key with users:read permission. Global API keys should use the legacy routes.

Request

x-api-key
string
required

Your user-specific API key with users:read permission (format: bna_hexstring)

Query Parameters

startDate
number

Unix timestamp for filtering jobs from this date

endDate
number

Unix timestamp for filtering jobs until this date

status
string

Filter jobs by status (e.g., “completed”, “pending”, “failed”)

type
string

Filter jobs by type (e.g., “linkedin_action”, “data_enrichment”)

Examples

Get All Jobs

curl -X GET "https://api.buena.ai/api/v2/users/jobs" \
  -H "x-api-key: bna_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Get Jobs with Date Range

curl -X GET "https://api.buena.ai/api/v2/users/jobs?startDate=1640995200&endDate=1703980800" \
  -H "x-api-key: bna_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Get Jobs by Status

curl -X GET "https://api.buena.ai/api/v2/users/jobs?status=completed" \
  -H "x-api-key: bna_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Response

userId
string

The authenticated user’s ID

success
boolean

Always true for successful requests

data
object

Job information and statistics

{
  "userId": "65e0a32640a597f6a7f897ea",
  "success": true,
  "data": {
    "jobs": [
      {
        "id": "job_123abc",
        "type": "linkedin_action",
        "status": "completed",
        "createdAt": "2024-01-20T10:00:00Z",
        "completedAt": "2024-01-20T10:02:00Z",
        "message": "Connection request sent successfully",
        "progress": 100,
        "metadata": {
          "actionType": "sendConnectionRequest",
          "profileUrl": "https://linkedin.com/in/johndoe",
          "delay": 75000
        }
      },
      {
        "id": "job_456def",
        "type": "data_enrichment",
        "status": "running",
        "createdAt": "2024-01-20T11:00:00Z",
        "completedAt": null,
        "message": "Processing lead enrichment batch",
        "progress": 60,
        "metadata": {
          "batchSize": 50,
          "processed": 30
        }
      }
    ],
    "statistics": {
      "total": 25,
      "pending": 3,
      "running": 2,
      "completed": 18,
      "failed": 2,
      "successRate": 85.7
    }
  }
}

Advanced Use Cases

1. Job Status Monitoring Dashboard

async function createJobsDashboard() {
  try {
    const response = await fetch("https://api.buena.ai/api/v2/users/jobs", {
      headers: {
        "x-api-key": process.env.BUENA_USER_API_KEY,
      },
    });

    const data = await response.json();

    // Display job statistics
    const stats = data.data.statistics;
    console.log(`📊 Job Statistics:`);
    console.log(`Total: ${stats.total}`);
    console.log(`✅ Completed: ${stats.completed}`);
    console.log(`⏳ Running: ${stats.running}`);
    console.log(`📋 Pending: ${stats.pending}`);
    console.log(`❌ Failed: ${stats.failed}`);
    console.log(`📈 Success Rate: ${stats.successRate}%`);

    // Show recent jobs
    const recentJobs = data.data.jobs
      .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
      .slice(0, 5);

    console.log(`\n🕐 Recent Jobs:`);
    recentJobs.forEach((job) => {
      const status = getStatusEmoji(job.status);
      console.log(`${status} ${job.type} - ${job.message} (${job.progress}%)`);
    });
  } catch (error) {
    console.error("Failed to fetch job data:", error);
  }
}

function getStatusEmoji(status) {
  const statusMap = {
    completed: "✅",
    running: "⏳",
    pending: "📋",
    failed: "❌",
  };
  return statusMap[status] || "❓";
}

// Update dashboard every 30 seconds
setInterval(createJobsDashboard, 30000);

2. Job Progress Tracking

import requests
import time
from datetime import datetime

class JobTracker:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = 'https://api.buena.ai/api/v2'

    def get_jobs(self, status=None, job_type=None):
        """Get jobs with optional filtering"""
        params = {}
        if status:
            params['status'] = status
        if job_type:
            params['type'] = job_type

        response = requests.get(
            f'{self.base_url}/users/jobs',
            headers={'x-api-key': self.api_key},
            params=params
        )
        return response.json()

    def wait_for_completion(self, job_id, timeout=300):
        """Wait for a specific job to complete"""
        start_time = time.time()

        while time.time() - start_time < timeout:
            jobs_data = self.get_jobs()
            job = next((j for j in jobs_data['data']['jobs'] if j['id'] == job_id), None)

            if not job:
                print(f"❌ Job {job_id} not found")
                return False

            if job['status'] == 'completed':
                print(f"✅ Job {job_id} completed successfully")
                return True
            elif job['status'] == 'failed':
                print(f"❌ Job {job_id} failed: {job['message']}")
                return False
            else:
                print(f"⏳ Job {job_id} progress: {job['progress']}% ({job['status']})")
                time.sleep(10)

        print(f"⏰ Timeout waiting for job {job_id}")
        return False

    def get_daily_summary(self):
        """Get today's job summary"""
        today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
        start_timestamp = int(today.timestamp())

        response = requests.get(
            f'{self.base_url}/users/jobs',
            headers={'x-api-key': self.api_key},
            params={'startDate': start_timestamp}
        )

        data = response.json()
        return {
            'date': today.strftime('%Y-%m-%d'),
            'statistics': data['data']['statistics'],
            'recent_jobs': data['data']['jobs'][:10]
        }

# Usage
tracker = JobTracker(os.getenv('BUENA_USER_API_KEY'))

# Monitor running jobs
running_jobs = tracker.get_jobs(status='running')
print(f"Currently running {len(running_jobs['data']['jobs'])} jobs")

# Get daily summary
summary = tracker.get_daily_summary()
print(f"Today's summary: {summary['statistics']}")

3. Automated Job Cleanup

async function cleanupFailedJobs() {
  const response = await fetch(
    "https://api.buena.ai/api/v2/users/jobs?status=failed",
    {
      headers: {
        "x-api-key": process.env.BUENA_USER_API_KEY,
      },
    }
  );

  const data = await response.json();
  const failedJobs = data.data.jobs;

  // Group failed jobs by type
  const failuresByType = failedJobs.reduce((acc, job) => {
    if (!acc[job.type]) acc[job.type] = [];
    acc[job.type].push(job);
    return acc;
  }, {});

  console.log("Failed Jobs Analysis:");
  Object.entries(failuresByType).forEach(([type, jobs]) => {
    console.log(`\n${type}: ${jobs.length} failures`);

    // Find common error patterns
    const errorMessages = jobs.map((j) => j.message);
    const uniqueErrors = [...new Set(errorMessages)];

    uniqueErrors.forEach((error) => {
      const count = errorMessages.filter((msg) => msg === error).length;
      console.log(`  - "${error}": ${count} occurrences`);
    });
  });

  // Identify jobs that might need retry
  const retryableJobs = failedJobs.filter(
    (job) =>
      job.message.includes("rate limit") ||
      job.message.includes("timeout") ||
      job.message.includes("network")
  );

  if (retryableJobs.length > 0) {
    console.log(`\n🔄 ${retryableJobs.length} jobs might be retryable`);
  }
}

// Run cleanup analysis daily
cleanupFailedJobs();

Legacy Routes

For backward compatibility, these legacy routes are still supported but require explicit user IDs:

  • GET /api/v2/users/jobs/userJobStatus/:userId - Get user job status
  • GET /api/v2/users/jobs/jobStatus - Get all jobs (requires jobs:read permission)
  • GET /api/v2/users/jobs/jobCounts - Get job counts only
  • PUT /api/v2/users/jobs/editJobMessage/:jobId - Edit job message (requires jobs:update)

Error Responses

Global API Key Used (400)

{
  "error": "When using global API key, please use the specific route: /api/v2/users/jobs/userJobStatus/:userId",
  "availableRoute": "/api/v2/users/jobs/userJobStatus/:userId"
}

Invalid Permission (403)

{
  "error": true,
  "code": "PERMISSION_DENIED",
  "message": "Insufficient permissions for user job access",
  "version": "2.0",
  "timestamp": "2024-01-20T15:30:00Z",
  "permissionHelp": {
    "required": "users:read",
    "available": ["linkedin:schedule"],
    "documentation": "https://docs.buena.ai/authentication"
  }
}

Next Steps