Advanced Features

Learn how to leverage Dispatched's advanced features to build robust background job processing for your applications.

Job Scheduling

Schedule jobs to run at any time within the next year using ISO 8601 UTC timestamps.

Scheduling Options

{
  "payload": {
    "task": "generate_report",
    "reportId": "annual-2024"
  },
  "scheduleFor": "2024-12-31T23:59:59Z"  // Run on New Year's Eve
}

Schedule Formatting Tips

  • Always use UTC timezone
  • Include full timestamp (YYYY-MM-DDTHH:mm:ssZ)
  • Maximum schedule: 1 year from current time
  • Minimum schedule: 1 minute from current time
  • Not providing scheduleFor will queue the job immediately

Example Use Cases

// Daily report at midnight UTC
{
  "payload": { "task": "daily_report" },
  "scheduleFor": "2024-12-18T00:00:00Z"
}

// Email reminder in 2 hours
{
  "payload": { "task": "send_reminder" },
  "scheduleFor": "2024-12-17T14:00:00Z"
}

Retry Management

Dispatched implements a smart retry system for failed jobs with exponential backoff.

Retry Policy

  • Maximum attempts: 3
  • Backoff intervals: 30s → 2m → 5m
  • Retry triggers:
    • Non-200/201 HTTP responses
    • Timeout exceeded
    • Network failures

Tracking Retries

Each webhook request includes retry information:

{
  "jobId": "job_abc123",
  "attemptId": "attempt_xyz456",
  "attemptNumber": 2,
  "payload": {
    "task": "process_video"
  }
}

Handling Retries

app.post('/webhook', async (req, res) => {
  const { jobId, attempt, payload } = req.body;

  try {
    // Log attempt number
    console.log(`Processing ${jobId} - Attempt ${attempt}/3`);

    // Implement custom retry logic if needed
    if (attempt > 1) {
      // Maybe use a different processing strategy
      await alternativeProcessing(payload);
    } else {
      await normalProcessing(payload);
    }

    res.status(200).json({ success: true });
  } catch (error) {
    // Will trigger retry if attempts remaining
    res.status(500).json({ error: error.message });
  }
});

Job Monitoring

Monitor your jobs through our dashboard and API.

Job Statuses

{
  QUEUED: "Job is waiting to be processed",
  STARTED: "Job processing has begun",
  DISPATCHED: "Job sent to webhook URL",
  COMPLETED: "Job successfully processed",
  CANCELLED: "Job manually cancelled",
  FAILED: "Job failed after all retries"
}

Dashboard Features

Access comprehensive job information at dispatched.dev/app/jobs:

  • Real-time job status
  • Full execution history
  • Retry attempts and outcomes
  • Payload inspection
  • Error logs and debugging info

Filter jobs by:

  • Status
  • Date range
  • Job ID
  • Custom payload properties

Timeout Management

Each subscription tier has specific timeout limits for job processing.

Timeout Limits

PlanMax Timeout
Starter30 seconds
Pro5 minutes
EnterpriseCustom

Handling Long-Running Jobs

For jobs that might exceed timeout limits:

  1. Break into smaller sub-jobs:
// Instead of one large job
{
  "payload": {
    "task": "process_large_video",
    "videoId": "123"
  }
}

// Break into chunks
{
  "payload": {
    "task": "process_video_chunk",
    "videoId": "123",
    "chunkIndex": 1,
    "totalChunks": 10
  }
}
  1. Use job chaining:
app.post('/webhook', async (req, res) => {
  const { payload } = req.body;

  // Process current chunk
  await processVideoChunk(payload);

  // Queue next chunk if needed
  if (payload.chunkIndex < payload.totalChunks) {
    await queueNextChunk({
      ...payload,
      chunkIndex: payload.chunkIndex + 1
    });
  }

  res.status(200).json({ success: true });
});

Production Best Practices

Idempotency

Design your webhook handlers to be idempotent:

app.post('/webhook', async (req, res) => {
  const { jobId, txn } = req.body;

  // Check if we've already processed this transaction
  if (await hasProcessedTransaction(txn)) {
    return res.status(200).json({ status: 'already_processed' });
  }

  // Process job
  await processJob(req.body);

  // Record transaction as processed
  await markTransactionProcessed(txn);

  res.status(200).json({ status: 'success' });
});

Error Handling

Implement comprehensive error handling:

app.post('/webhook', async (req, res) => {
  try {
    // Validate webhook signature
    validateWebhookSignature(req);

    // Process the job
    await processJob(req.body);

    res.status(200).json({ status: 'success' });
  } catch (error) {
    // Log error details
    console.error(`Job ${req.body.jobId} failed:`, error);

    if (error.retryable) {
      // Return 500 to trigger retry
      res.status(500).json({ error: error.message });
    } else {
      // Mark as completed but failed
      res.status(200).json({
        status: 'failed',
        error: error.message
      });
    }
  }
});

Next Steps

Need help? Contact our support team at support@dispatched.dev