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 (v2):

{
  "id": "1705312200000",
  "status": "success",
  "assetUrl": "https://storage.cloudlayer.io/assets/abc123/document.pdf",
  "timestamp": 1705312200000
}

Note: v1 synchronous requests return the raw binary file (PDF, image, etc.) directly in the response body — not JSON. The JSON response shown above applies only to v2.

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 the webhook URL you provide in the request.

{
  "url": "https://example.com",
  "async": true,
  "webhook": "https://yourserver.com/webhooks/cloudlayer"
}

Immediate response:

{
  "status": "pending",
  "id": "1705312200000"
}

Webhook delivery (when complete):

{
  "id": "1705312200000",
  "status": "success",
  "type": "url-pdf",
  "assetUrl": "https://storage.cloudlayer.io/assets/abc123/document.pdf",
  "previewUrl": "https://storage.cloudlayer.io/assets/abc123/document-preview.webp",
  "processTime": 2340,
  "size": 184320,
  "timestamp": 1705312200000
}

Note: The webhook payload includes the full job object. Additional internal fields (cost data, worker info, etc.) may also be present but are not part of the public API contract.

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

Providing Your Webhook URL

In v2 of the API, you provide a webhook URL directly in each request using the webhook parameter. When the async job completes, cloudlayer.io sends the result to that URL.

{
  "url": "https://example.com",
  "async": true,
  "webhook": "https://yourserver.com/webhooks/cloudlayer"
}

The webhook URL must use HTTPS.

Webhook Endpoint Requirements

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:

{
  "id": "1705312200000",
  "status": "success",
  "type": "url-pdf",
  "assetUrl": "https://storage.cloudlayer.io/assets/abc123/document.pdf",
  "previewUrl": "https://storage.cloudlayer.io/assets/abc123/document-preview.webp",
  "processTime": 2340,
  "size": 184320,
  "timestamp": 1705312200000
}

Error payload:

{
  "id": "1705312200000",
  "status": "error",
  "type": "url-pdf",
  "error": "Timeout: page took longer than 30000ms to load",
  "timestamp": 1705312205000
}

Payload Fields

FieldTypeDescription
idstringUnique identifier for the generation job
statusstringJob outcome: success or error
typestringThe job type (e.g., url-pdf, html-image, template-pdf)
assetUrlstringURL to the generated document (on success)
previewUrlstringURL to the preview thumbnail, if generatePreview was used
processTimenumberProcessing time in milliseconds
sizenumberFile size of the generated document in bytes
errorstringError description (on failure)
timestampnumberUnix epoch timestamp in milliseconds of when the job completed

Example Webhook Handler

Node.js (Express)

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

  if (status === "success") {
    console.log(`Job ${id} completed: ${assetUrl}`);
    // Process the generated document -- download it, email it, etc.
  } else {
    console.error(`Job ${id} 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["id"]
    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

Webhook Delivery

cloudlayer.io makes a single delivery attempt to your webhook URL. There are no automatic retries. If your endpoint is unreachable or returns a non-200 status code, the webhook delivery fails silently — the job status is not affected. The job remains success or error based on the generation result.

If webhook delivery fails, 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. Use the id to deduplicate in case of unexpected duplicate deliveries.
  • Implement your own fallback. Since there are no automatic retries, consider polling the Jobs API as a backup if webhook delivery is critical to your workflow.
  • 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 id 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