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
assetUrlis 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
| Scenario | Recommended Mode | Reason |
|---|---|---|
| Simple, fast conversions (single page) | Sync | Simpler implementation, immediate result |
| User-facing “download PDF” button | Sync | User expects immediate response |
| Batch processing (multiple documents) | Async | Avoids timeouts, handles volume |
| Large or complex documents | Async | May exceed sync timeout limits |
| Background/scheduled generation | Async | No user waiting for the result |
| High-volume automated pipelines | Async | Better throughput, reliable delivery |
| Pages with heavy JavaScript rendering | Async | Render 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:
- Accept HTTP POST requests
- Return a
200status code to acknowledge receipt - 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
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the generation job |
status | string | Job outcome: success or error |
type | string | The job type (e.g., url-pdf, html-image, template-pdf) |
assetUrl | string | URL to the generated document (on success) |
previewUrl | string | URL to the preview thumbnail, if generatePreview was used |
processTime | number | Processing time in milliseconds |
size | number | File size of the generated document in bytes |
error | string | Error description (on failure) |
timestamp | number | Unix 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
200quickly. 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
idto 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
timeoutparameter 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.