Events
Webhooks
Webhook configuration, payload, signature verification, timeout, and the two independent retry mechanisms behind asynchronous document generation.
A document request never returns the finished document inline. Doclift delivers it later as a webhook, once every generation in the request has finished.
Configuration
There is no API endpoint to register, list, or rotate a webhook URL. The
delivery target is a single field, Webhook URL, set on the external
application from the Doclift dashboard (the same key you send as
X-Api-Key).
A request is refused outright if the calling external application has no webhook URL configured:
{
"error": "Please add a webhook URL to this external application to use asynchronous generation. You can add one from your Doclift.io account."
}
When it fires
Once every document_generations entry in the request has finished (each
one either success or error), Doclift sends exactly one webhook for the
whole request. There is no per-generation webhook and no separate "started"
event.
Payload
POST https://yourapp.com/webhooks/doclift
Content-Type: application/json
Accept: application/json
X-Doclift-Signature: sha256=<hex hmac>
{
"id": 100004,
"tag": "Batch#12",
"type": "asynchrone",
"timestamp": "2024-01-15T10:35:00+01:00",
"documents_generations": [
{
"id": 100230,
"tag": "Subscription#102",
"generated_at": "2024-01-15T10:35:02+01:00",
"created_at": "2024-01-15T10:35:00+01:00",
"generation_status": "success",
"file": {
"filename": "Contract-1615737313.pdf",
"url": "https://doclift.s3.eu-west-1.amazonaws.com/d207f8fe.pdf?[...]",
"size": 40290
}
}
]
}
The documents_generations entries carry the same fields documented in
The document request object.
The file url is a signed link that needs no authentication to fetch,
valid for 2 hours; after that, fetch the document again through
View a document request,
whose own file URLs stay valid for 20 hours.
Signature verification
Every webhook request carries X-Doclift-Signature: sha256=<hex hmac>: an
HMAC-SHA256 of the raw request body, keyed with your external application's
secret_key. That is the same value you send as X-Api-Key; there is no
separate signing secret.
computed = "sha256=" + OpenSSL::HMAC.hexdigest(
"SHA256",
api_secret_key,
request.raw_post,
)
valid = ActiveSupport::SecurityUtils.secure_compare(
computed,
request.headers["X-Doclift-Signature"].to_s,
)
Compute the HMAC over the raw body, not a re-serialized copy of the parsed JSON. Whitespace or key ordering differences would produce a different signature.
What your endpoint must answer
Respond with an HTTP 2xx status within 3 seconds. Any other status code, or no response inside that window, counts as a failed delivery attempt and feeds the retry policy below.
Retry policy
Two separate systems can retry around a single request, and they answer two different questions. Conflating them will make you misread either one.
| Webhook delivery retries | PDF generation retries | |
|---|---|---|
| Question it answers | Did your endpoint acknowledge the webhook? | Did generation succeed? |
| Configurable how | webhooks_replays_count, per organization (default 3); contact Doclift to change it | Not caller-configurable |
| Visible via | Nothing in this API (see below) | The generation's own generation_status/generation_error |
| Independent of | Whether the document rendered | Whether your endpoint ever answers |
Webhook delivery retries apply once generation has already finished and Doclift is trying to hand you the result. A non-2xx response, or a timeout/transport failure, schedules another delivery attempt with exponential backoff:
delay for retry attempt n (1, 2, 3, ...) = min(30 × 2^(n-1), 1800) seconds
30s, 60s, 120s, 240s, ... capped at 30 minutes. webhooks_replays_count
(see User info) is the number of
retries on top of the first attempt. A default of 3 means up to 4
delivery attempts in total before Doclift gives up. A value of 0 or less
disables retrying entirely.
PDF generation retries happen earlier and independently, before there
is anything to deliver. This has nothing to do with your webhook endpoint
and nothing to do with webhooks_replays_count. A document that renders
successfully still triggers exactly one, ordinary webhook delivery once
it's done.
Failure visibility
The document request's status field reflects generation outcome
(success once every generation succeeded, error otherwise). It says
nothing about whether the webhook was ever successfully delivered to your
endpoint. There is no field or endpoint in this API that reports webhook
delivery outcome. If your endpoint never returns a 2xx and retries are
exhausted, the underlying document still exists and can be fetched with
View a document request.