Callbacks
A callback is a per-request, outbound notification you attach to an order so
CloudPress POSTs to your URL the moment the order's async work finishes —
instead of you polling the task. You supply the callback when you place the
order; CloudPress fires it once that order's task reaches a terminal
state (OK, FAILED, or CANCELLED).
Callback vs. billing webhook
A callback is per request — it belongs to the single order you attach it to, and fires once. A billing webhook is plan-level — configured by an admin on the billing plan, it fires on every service lifecycle event under that plan. Different mechanisms; see the comparison below.
How it works
- You place an order with a
callbackobject (POST /api/orders,POST /api/orders/domain, orPOST /api/cpanel_accounts). - CloudPress accepts the order (
202 Accepted) and provisions asynchronously as a task. - When the task completes (or fails), CloudPress sends an HTTP
POSTto your callbackurl. - If you don't register a callback, poll
GET /api/tasks/{task-id}instead.
Delivery is retried but not guaranteed. A delivery that completes with a
non-2xx status is retried, so your receiver must be idempotent — but a
delivery that never completes at all (connection refused, timeout) is not
retried and is lost. Read Delivery & retries before you
build on callbacks as your only signal.
Registering a callback
Add a callback object to the order body. Three endpoints accept it:
POST /api/orders— site ordersPOST /api/orders/domain— domain registration / transfer ordersPOST /api/cpanel_accounts— cPanel hosting account orders
Params
- callback: Object (optional) | Fired when the order's async task completes.
- url: String (required) | Fully-qualified HTTPS URL that receives the POST.
- authorization: String (optional) | Sent verbatim as the
Authorizationheader on the callback. Supply the full value — e.g.Bearer 12345,Token 12345, or a raw token.
curl -X POST https://my.cloudpress.com/api/orders \
-H "Authorization: Bearer $CLOUDPRESS_TOKEN" \
-H "X-Auth-Account: $ACCOUNT_ID" \
-H "Content-Type: application/json" \
-d '{
"order": { "plan": "basic", "term": "monthly" },
"callback": {
"url": "https://your-app.example.com/cloudpress/callback",
"authorization": "Bearer your-shared-secret"
}
}'
What CloudPress sends
When the task finishes, CloudPress sends a POST to your url:
- Headers:
Authorization: <your authorization value>(verbatim; omitted if you didn't supply one) andAccept: application/json. - Body: JSON.
{
"timestamp": 1727303593,
"success": true,
"data": "Site provisioned. Primary domain: example.com"
}
Body Params
- timestamp: Integer | Unix epoch (seconds) at which this delivery was queued. Stable across the retries of one delivery, but not a unique event key — see Delivery & retries.
- success: Boolean |
truewhen the task endedOK;falsewhen it endedFAILEDorCANCELLED. - data: String | The task's
datafield — the same free-form result text returned byGET /api/tasks/{task-id}.
success: false does not always mean the task failed
A cancelled task fires the callback too, and reports "success": false
— byte-for-byte indistinguishable from a genuine failure. The body carries
no status field to tell them apart. If the distinction matters to you, read
the task with GET /api/tasks/{task-id}, where CANCELLED and FAILED are
separate values.
Delivery & retries
Callbacks share CloudPress's outbound delivery layer (the same one used by billing webhooks).
A timeout or a refused connection is never retried
Only deliveries that complete as an HTTP response with a non-2xx
status enter the retry schedule. If the request never completes at all —
connection refused, DNS or TLS failure, or the 30-second timeout elapsing —
the delivery is attempted once and then abandoned permanently. Nothing
is re-queued and nothing is re-sent, so a receiver that is unreachable for
even a moment loses that callback for good.
Treat callbacks as best-effort for exactly this reason. For anything you
cannot afford to miss, keep a periodic poll of GET /api/tasks/{task-id}
as the backstop rather than relying on the callback alone.
- Timeout: 30 seconds per attempt.
- Success: any HTTP
2xx. A completed non-2xxresponse is a failed delivery and is retried. A connection failure or timeout is not — see the warning above. - Retries: the first retry is sent 5 minutes after the original attempt, and every 15 minutes after that.
- Giving up: retries stop 4 hours after the first attempt.
- Idempotency: retries mean the same callback may arrive more than once, so
your receiver must be idempotent.
timestampis not a safe de-duplication key — it is stable across the retries of a single delivery, but a task that is finalized more than once (the provisioning job and an inboundPOST /api/webhooks/task/{task-id}each trigger a delivery) produces a freshtimestampfor the same result. The body carries no task id either, so correlate on something you control: put an order or task reference in the callbackurlyou register, and make the handler safe to run twice.
Everything below is the inbound direction
The callback described above is outbound — CloudPress POSTing to your
URL. The remaining endpoints on this page are the mirror image: /api/webhooks/*
routes that CloudPress's own provisioning infrastructure calls into the
API. They are documented here because posting to them is what triggers the
outbound callback above, but they are not part of an integrator's normal
flow — they authenticate with a back-end system API key and are
blocked for OAuth tokens. Don't confuse the two directions.
Testing reachability
GET /api/webhooks/task
Returns the source IP CloudPress sees for your request, so you can confirm network reachability / allow-listing before relying on a callback.
Returned Params
- ip_address: String
An identical probe exists on the cache-webhook path —
GET /api/webhooks/cdn_cache.
Reporting a task result
POST /api/webhooks/task/{task-id}
This is the endpoint that marks a task complete inside CloudPress — it is how
provisioning infrastructure reports a result back, and posting that result is
what triggers your registered outbound callback. It updates the task's data,
sets its status (OK / FAILED), and then fires the callback.
Account Scope Required (X-Auth-Account). Not available via OAuth — this
endpoint authenticates with a back-end API key (it is normally called by
CloudPress infrastructure, not by integrators).
Always returns 200, even for an unknown task ID. Idempotent: a repeat post
with the same data + success is de-duplicated by digest and is a no-op.
Params
- data: Any (optional) | Appended to the task's
data. - success: Boolean (required) |
true→ taskOK;false→ taskFAILED.
Requesting a CDN cache purge
POST /api/webhooks/cdn_cache
The inbound endpoint a site's own hosting container uses to ask CloudPress to
purge that site's CDN cache — for example when a WordPress caching plugin flushes
the page cache after a publish. CloudPress resolves the site, opens a
task named site.cache.cdn_purge, and performs the purge
asynchronously.
Not available via OAuth. This endpoint authenticates with a back-end,
IP-allow-listed system API key — it is called by CloudPress infrastructure,
not by integrators. An OAuth token is rejected with 403
insufficient_scope, so a third-party app can never purge a customer's CDN
on behalf of infrastructure.
Params
- cs_project_id: Integer (required) | The site's internal project id. Must be a positive integer.
- mode: String (required) |
allto purge the whole pull zone, orpathsto purge specific paths. - paths:
Array<String>(required whenmodeispaths) | Up to 100 entries. A path ending in/or containing*is a prefix purge; anything else purges that exact object.
{ "cs_project_id": 4821, "mode": "paths", "paths": ["/", "/blog/*"] }
Returned Params
- status: String | Always
"accepted". - task_id: Integer | The purge task to poll — present when a purge was enqueued.
- noop: Boolean |
truewhen there was nothing to purge;task_idis then absent.
202 Accepted means accepted, not "purge confirmed" — the purge runs out of
band and retries internally on transient CDN errors. An unknown site, or a site
with no active CDN, is likewise an accepted no-op
({"status":"accepted","noop":true}) rather than an error, so the caller never
has to tell "site gone" apart from "purged".
Because the caller is infrastructure rather than a person, the resulting task is
attributed to System in the site's task history.
Errors
- 400
invalid_cs_project_id|cs_project_idmissing, or not a positive integer. - 400
invalid_mode|modeis neitherallnorpaths. - 400
missing_paths|modeispathsbut no usable path was supplied. - 400
too_many_paths| more than 100 entries inpaths. - 401 | authentication or IP allow-list failure.
- 403
insufficient_scope| an OAuth token was used.
To purge a site's CDN cache as an integrator, use the ordinary per-site endpoints instead — see Purge the Cache and Purge a Cache Layer.
Testing cache-webhook reachability
GET /api/webhooks/cdn_cache
The same connectivity probe as GET /api/webhooks/task,
on the cache-webhook path.
Returned Params
- ip_address: String
Callbacks vs. billing webhooks
| Callback | Billing webhook | |
|---|---|---|
| Configured on | Per request, in the callback object on an order |
The billing plan (admin UI) |
| Scope | The single task / order it was attached to | Site, cPanel-account & domain lifecycle events under that plan |
| Fires | Once, when that task reaches OK / FAILED / CANCELLED |
On every created / resized / registered / renewed / … event |
| Body | { "timestamp", "success", "data" } (the task result) |
{ "action", "site" }, { "action", "cpanel_account_link" } or { "action", "domain" } |
See Billing Webhooks for the plan-level mechanism.