Webhook After Certificate Validation
This webhook delivers the content of a case as soon as it is closed: collected items, media, certificate URL, and the business context you provided when the case was created (references, beneficiary, metadata).
The analysis result webhook delivers the case's trust verdict (score, tampering detection, document analyses). It is sent later, once the analysis is complete. Both are independent and can be enabled together.
When it is sent
The webhook is sent when the case is closed, whatever the channel that triggers it:
| Channel | Trigger |
|---|---|
| Mobile application | The user validates the case, including a case opened from a Certilink. |
| API | GET /cases/close/:caseId or GET /cases/close/certilink/:magicLinkToken (see Case management). |
| Mobile SDK | sendPhoto() creates and closes the capture's case. takePhoto() only performs local capture. |
A case triggers one delivery per closure.
Configuration
From your backend
You configure the webhook URL and token yourself through the API, without any action from Certificall.
POST /company-options/api-settings
Authentication: Authorization: Bearer <token> (see Authentication). The API account must have the permission to update the company.
Request body
| Name | Type | Required | Description |
|---|---|---|---|
endpoint | String | yes | HTTPS URL of your endpoint, able to receive POST requests. |
authToken | String | yes | Token that Certificall will present on each call. |
authorizationHeader | Enum | no | Where the token goes: authorization (default, Authorization: Bearer header) or security-token (dedicated header). |
Example
curl -X POST https://admin.certificall.app/certificall/api/company-options/api-settings \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"endpoint": "https://your-domain.com/webhooks/certificall/case",
"authToken": "<token Certificall will present to you>",
"authorizationHeader": "authorization"
}'
Response 200
{
"companyId": 42,
"apiSettings": [
{
"apiName": "caseHook",
"endpoints": { "sendCase": "https://your-domain.com/webhooks/certificall/case" },
"authorizationHeader": "authorization",
"hasCredentials": true
}
]
}
The token is never displayed again once saved: the response and GET /company-options only indicate whether credentials exist (hasCredentials). To change it, send the request again with the new token.
URL constraints
The following are rejected: non-https URLs, literal IP addresses (v4 or v6), and hosts without a domain name. Your endpoint must be publicly exposed, on a resolvable domain.
By Certificall
The HMAC-SHA256 (apiKey + apiSecretKey) and dedicated API key (Make-style integrations) authentication modes cannot be configured through the API. Send your Certificall contact the URL of your endpoint and the credentials to use; they will be registered on your company.
Authenticating the incoming call
Depending on the configured mode, your endpoint receives one of the following headers:
# Bearer (default)
Authorization: Bearer <authToken>
Content-Type: application/json
# Dedicated header
security-token: <authToken>
Content-Type: application/json
# API key (Make-style integrations)
x-make-apikey: <apiKey>
Content-Type: application/json
# HMAC-SHA256
Authorization: HMAC-SHA256 <apiKey>:<signature>
X-Api-Key: <apiKey>
Content-Type: application/json
For the HMAC mode, the signature is an HMAC-SHA256 of the JSON body exactly as sent, computed with your apiSecretKey. Verify it against the raw request body, not a re-serialization.
Basic authentication (username and password) is not offered by this webhook.
Payload
{
"cfRef": "CAS-12345_CMP-42",
"createdAt": "2026-05-13T08:24:15.000Z",
"reportId": "SITE-2026-001",
"frame": "Car damage report",
"closed": true,
"caseUrl": "https://admin.certificall.app/certificall/share/case/<shareToken>",
"items": [
{
"createdAt": "2026-05-13T08:22:01.000Z",
"cfRef": "<item reference>",
"title": "Vehicle photo",
"description": "vehicle_001.jpg",
"stepAction": "PHOTOGRAPHY",
"geolocLatitude": "48.8566",
"geolocLongitude": "2.3522",
"imageUrl": "https://admin.certificall.app/certificall/images/<identifier>"
},
{
"createdAt": "2026-05-13T08:23:10.000Z",
"cfRef": "<item reference>",
"title": "Plate number",
"description": "AB-123-CD",
"stepAction": "TEXT_FIELD",
"geolocLatitude": "48.8566",
"geolocLongitude": "2.3522",
"imageUrl": ""
}
],
"context": {
"beneficiaire": { "nom": "Dupont", "prenom": "Jean", "adresse": "123 rue de la Paix", "cp": "75001", "ville": "Paris" },
"emetteur": "ACME Company",
"reportRef": "FILE-2025-001",
"instructions": "Check the overall condition of the equipment",
"metadata": { "fileId": "FILE-2025-001", "agency": "PAR-09" }
}
}
Envelope
| Field | Type | Description |
|---|---|---|
cfRef | String | Public reference of the case, formatted CAS-<id>_CMP-<company>. Unique: this is your deduplication key. |
createdAt | ISO 8601 | Case creation date. |
reportId | String | The reportToken you provided when creating the case, the Certilink or the SDK photo. This is your matching key. |
frame | String | Name of the case's frame. |
closed | Boolean | Always true at delivery time. |
caseUrl | URL | URL of the PDF certificate. null if the case has no share link. |
items | Array | The collected items. |
context | Object | The business context provided at creation. Absent if no context was provided. |
Items
| Field | Type | Description |
|---|---|---|
createdAt | ISO 8601 | Collection date of the item. |
cfRef | String | Unique reference of the item. |
title | String | Title of the step in the frame. |
description | String | Collected value: entered text, date, choice, or file title for a photo. |
stepAction | Enum | Step type: PHOTOGRAPHY, VIDEO, SIGNATURE, DOCUMENT_UPLOAD, TEXT_FIELD, NUMBER_FIELD, DATE_FIELD, TIME, EMAIL, SELECT. |
geolocLatitude, geolocLongitude | String | Coordinates of the collection. |
imageUrl | URL | Image URL for a PHOTOGRAPHY or a SIGNATURE, empty string otherwise. |
extractedData | Array | Present only if field extraction is configured on the step: list of { key, label, value }. |
Context
The content of context depends on how the case was created:
| Case origin | Content of context |
|---|---|
POST /cases/create or PATCH /cases/update with a caseContext | The visibleData fields under their internal keys: beneficiaire (nom, prenom, adresse, cp, ville), emetteur, photoRef, reportRef, instructions, user. Only the keys you filled in are present. |
Certilink (POST /certilink/create with a context) | The Certilink's context object, returned as is. |
| Application without context | context absent. |
Metadata
metadata is a field of the case, next to context: your key/value pairs exactly as supplied, whether the case comes from POST /cases/create, a Certilink or the mobile SDK. The key is absent when the case carries none.
To match the case with your system, use reportToken (returned as reportId), available on every channel and the only field the API filters on. The metadata field is a complement, returned on every channel: site identifier, agency code, file type, originating page…
Delivery guarantees
| Behaviour | |
|---|---|
| Retries | None. If your endpoint is unreachable or responds with an error, the failure is logged on Certificall's side and the delivery is not retried. |
| Duplicates | One delivery per closure. Still keep an idempotent handling on cfRef. |
| Order | Not guaranteed across cases. |
Two practical consequences:
- Respond with a 2xx quickly and do your business processing in the background.
- Plan a safety net. A periodic sweep of
GET /cases?format=metadata&closed=true&hours=…(see Case management) catches cases whose webhook did not reach you.
Handling on the partner side
// POST /webhooks/certificall/case (your route)
app.post('/webhooks/certificall/case', verifyToken, async (req, res) => {
const { cfRef, reportId, caseUrl, items, context } = req.body;
// 1) Acknowledge immediately
res.status(200).end();
// 2) Find your record: reportId = your reportToken
const record = await db.findByReference(reportId);
if (!record) return; // unknown record: ignore, no error
// 3) Idempotency on cfRef
if (await db.alreadyReceived(cfRef)) return;
// 4) Use the content
const photos = items.filter(i => i.stepAction === 'PHOTOGRAPHY').map(i => i.imageUrl);
const metadata = context?.metadata ?? context?.visibleData?.metadata ?? {};
await db.attachCertificate(record.id, { cfRef, caseUrl, photos, metadata });
});
Checklist
- Endpoint publicly exposed over HTTPS, on a resolvable domain.
- URL and token registered through
POST /company-options/api-settings(or sent to Certificall for HMAC and API key). - Authentication mode verified on reception.
-
reportTokenset when creating your cases, Certilinks or SDK photos. - Idempotent handling on
cfRef. - Quick 2xx response, business processing decoupled.
- Safety net on
GET /casesfor sensitive cases.
Going further
- Analysis result webhook — the case's trust verdict
- Integrating a "Certify" button — the full Certilink → webhook journey