API

Webhooks & Async Processing

cloudlayer.io supports both synchronous and asynchronous processing modes. Understanding when to use each — and how to configure webhooks for async results — is key to building reliable document generation workflows.

Sync vs Async Processing

Synchronous Mode (async: false)

In synchronous mode, the API waits for the document to be generated and returns the result directly in the HTTP response.

{
  "url": "https://example.com",
  "async": false
}

Response:

{
  "assetUrl": "https://storage.cloudlayer.io/assets/abc123/document.pdf",
  "status": "success",
  "jobId": "job_abc123def456"
}

Characteristics:

  • The HTTP connection stays open until the document is ready
  • The assetUrl is populated in the response
  • Simpler to implement — no webhook setup needed
  • Subject to connection timeouts for long-running jobs

Asynchronous Mode (async: true, default)

In asynchronous mode, the API immediately returns a job ID and processes the document in the background. When complete, the result is delivered to your webhook URL.

{
  "url": "https://example.com",
  "async": true
}

Immediate response:

{
  "status": "queued",
  "jobId": "job_abc123def456"
}

Webhook delivery (when complete):

{
  "jobId": "job_abc123def456",
  "status": "success",
  "assetUrl": "https://storage.cloudlayer.io/assets/abc123/document.pdf",
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Characteristics:

  • The HTTP connection closes immediately after queuing
  • No risk of connection timeouts
  • Results delivered via webhook callback
  • Better for long-running and batch operations

When to Use Each Mode

ScenarioRecommended ModeReason
Simple, fast conversions (single page)SyncSimpler implementation, immediate result
User-facing “download PDF” buttonSyncUser expects immediate response
Batch processing (multiple documents)AsyncAvoids timeouts, handles volume
Large or complex documentsAsyncMay exceed sync timeout limits
Background/scheduled generationAsyncNo user waiting for the result
High-volume automated pipelinesAsyncBetter throughput, reliable delivery
Pages with heavy JavaScript renderingAsyncRender time may be unpredictable

Webhook Configuration

Setting Up Your Webhook Endpoint

Your webhook endpoint is a URL on your server that cloudlayer.io calls when an async job completes. Configure it in your cloudlayer.io dashboard under Settings.

Your endpoint must:

  1. Accept HTTP POST requests
  2. Return a 200 status code to acknowledge receipt
  3. Process the webhook payload

Webhook Payload

When a job completes, cloudlayer.io sends a POST request to your webhook URL with a JSON body:

Success payload:

{
  "jobId": "job_abc123def456",
  "status": "success",
  "assetUrl": "https://storage.cloudlayer.io/assets/abc123/document.pdf",
  "previewUrl": "https://storage.cloudlayer.io/assets/abc123/document-preview.webp",
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Error payload:

{
  "jobId": "job_abc123def456",
  "status": "error",
  "error": "Timeout: page took longer than 30000ms to load",
  "timestamp": "2024-01-15T10:30:05.000Z"
}

Payload Fields

FieldTypeDescription
jobIdstringUnique identifier for the generation job
statusstringJob outcome: success or error
assetUrlstringURL to the generated document (on success)
previewUrlstringURL to the preview thumbnail, if generatePreview was used
errorstringError description (on failure)
timestampstringISO 8601 timestamp of when the job completed

Example Webhook Handler

Node.js (Express)

app.post("/webhooks/cloudlayer", (req, res) => {
  const { jobId, status, assetUrl, error } = req.body;

  if (status === "success") {
    console.log(`Job ${jobId} completed: ${assetUrl}`);
    // Process the generated document -- download it, email it, etc.
  } else {
    console.error(`Job ${jobId} failed: ${error}`);
    // Handle the error -- retry, notify, log, etc.
  }

  // Always return 200 to acknowledge receipt
  res.status(200).send("OK");
});

Python (Flask)

from flask import Flask, request

app = Flask(__name__)

@app.route("/webhooks/cloudlayer", methods=["POST"])
def handle_webhook():
    payload = request.get_json()
    job_id = payload["jobId"]
    status = payload["status"]

    if status == "success":
        asset_url = payload["assetUrl"]
        print(f"Job {job_id} completed: {asset_url}")
        # Process the generated document
    else:
        error = payload.get("error", "Unknown error")
        print(f"Job {job_id} failed: {error}")
        # Handle the error

    return "OK", 200

Retry Behavior

If your webhook endpoint does not return a 200 status code, cloudlayer.io will retry delivery:

  • Retry attempts: Up to 3 retries
  • Retry interval: Exponential backoff (increasing delay between attempts)
  • Final failure: If all retries fail, the webhook delivery is marked as failed. You can still retrieve the job result via the jobs API.

Ensuring Reliable Delivery

  • Return 200 quickly. Process the webhook payload asynchronously (e.g., push to a queue) rather than doing heavy work inside the handler.
  • Make your handler idempotent. Webhooks may be delivered more than once in edge cases. Use the jobId to deduplicate.
  • Log all webhook deliveries for debugging and auditing.

Best Practices

Use Async for Production Pipelines

Synchronous mode is convenient for development and simple use cases, but async with webhooks is more robust for production:

  • No risk of HTTP timeouts cutting off long-running jobs
  • Your application does not need to hold open connections
  • Better scalability for high-volume generation

Correlate Jobs with Your Data

Pass a projectId or use a naming convention in your filename to correlate generated documents with your internal records:

{
  "url": "https://example.com/invoice/INV-001",
  "filename": "invoice-INV-001.pdf",
  "projectId": "N77VCoAVTHHwgmUfCYlD",
  "async": true
}

When the webhook fires, use the jobId to look up which internal record triggered the generation.

Handle Errors Gracefully

Your webhook handler should account for:

  • Timeout errors — the page took too long to render. Consider increasing the timeout parameter or simplifying the page.
  • Navigation errors — the URL was unreachable. Verify the URL is accessible and correctly spelled.
  • Rendering errors — the page had JavaScript errors or missing resources. Test the page in a browser first.

Monitor Webhook Health

  • Set up alerts for webhook delivery failures
  • Track success/failure ratios over time
  • Use a service like Hookdeck or ngrok for local webhook testing during development