Originality Webhooks
Webhooks in Originality notify your systems when documents and reports change, so you can react to events instead of polling the API.
When a subscribed event occurs, Originality sends a signed HTTP POST to the endpoint you have configured. Each delivery is signed with HMAC-SHA256 so you can verify it came from us and was not altered in transit.
Webhook endpoints are configured per licence in the Originality user interface. They are not managed through the public API.
In this article:
- How to configure Originality webhooks
- Delivery behavior
- Request structure
- Available events
- Troubleshooting
How to configure Originality webhooks
- In Originality, go to License settings > Webhooks tab.
- Select Add Webhook.
- Enter your Endpoint URL. It must be a valid HTTPS URL.
- Under Event Subscriptions, select the events you want to receive. All five are selected by default – deselect the ones you do not need, or use Clear all and pick individually.
- Select Create & Continue.
- The signing secret is displayed. Copy the secret and store it securely – it is only shown once.
You can configure up to five webhook endpoints per license. The limit applies to the license as a whole, not per user, so endpoints created by different administrators count towards the same total.
To change an endpoint URL or its subscriptions, edit the webhook in the same tab. The signing secret is not regenerated when you edit, and it cannot be displayed again – if you lose it, delete the webhook and create a new one.
Delivery behavior
At-least-once delivery. Your endpoint may receive the same event more than once. Use the X-Originality-Delivery-ID header to deduplicate, and make sure your handler produces the same result when it processes the same delivery twice.
No guaranteed ordering. Events are delivered from a queue and are not ordered. Do not assume document.created arrives before document.report.created for the same document. Where order matters, use the createdAt field and your own record of the document's state rather than arrival order.
Respond quickly. Return a 2xx status within 10 seconds. Any response outside the 2xx range, or no response at all, counts as a failed delivery. Acknowledge first and do the work on a background queue.
Retries. Failed deliveries are retried automatically with exponential back-off for up to 16 hours after the first attempt. After that the event is permanently discarded and will not be delivered again.
No delivery history. Originality does not store a log of webhook deliveries that you can inspect. If your endpoint is down beyond the retry window, those events are gone — if such case occurs, reconcile the affected documents through the API.
Request structure
Every delivery is a POST with a JSON body.
Headers
| Header | Description |
| Content-Type | Always application/json. |
| User-Agent | Always originality-hookshot/1.0. Useful for allow-listing our traffic. |
| X-Originality-Delivery-ID | Identifies this delivery. Use it to deduplicate retried events. |
| X-Originality-Signature-256 | HMAC-SHA256 of the raw request body, base64-encoded. |
| X-Originality-Resource | The resource the event concerns: document or document.report. |
| X-Originality-Action | What happened: created, updated or deleted. |
| X-Originality-Webhook-ID | Which of your configured endpoints this was sent to. Useful when several endpoints share one handler. |
| X-Originality-Timestamp | When the event was produced, in UTC. |
Determining the event type. The body does not contain an event name. Combine the two headers to get the event type — X-Originality-Resource: document.report plus X-Originality-Action: created is document.report.created. Route on the headers, not on the shape of the body: document.report.created and document.report.deleted carry structurally identical bodies and differ only in whether report is populated.
Body
The body is the affected object itself, with no wrapping envelope:
{
"id": 1637462,
"filename": "Eigenvalues and Eigenvectors Problems 1.pdf",
"createdAt": "2026-06-08T08:19:18.094412Z",
"report": {
"status": "complete",
"score": 1,
"opened": false
},
"context": null,
"settings": {
"analyze": true,
"index": true
}
}
| Field | Type | Description |
| id | integer | The Originality document ID. Use this to correlate with your own records. |
|
filename |
string | The submitted file name. |
| createdAt | string | When the document was created. |
| report | object or null | The report, when one exists. null before processing completes and after a report is deleted. |
| report.status | string | Terminal processing state — complete (report ready), available (indexed, no report produced) or failed (processing did not succeed). |
| report.score | number | The similarity score. |
| report.opened | boolean | Whether the report has been opened by a user. |
| context | object or null | Integration context, where the submitting system supplied one. |
| settings.analyze | boolean | Whether the document was analysed for similarity. |
| settings.index | boolean | Whether the document was indexed as a future source. |
Available events
Document Created – Triggered when a new document is submitted. The report has not been produced yet, so report is null.
{
"id": 1234567,
"filename": "LAE-Assign-Prob.pdf",
"createdAt": "2026-06-09T06:36:00.528539141Z",
"report": null,
"context": null,
"settings": {
"analyze": true,
"index": false
}
}
Document Deleted – Triggered after a document has been fully removed, including its database record, stored files and vector data. Only the ID is sent, because nothing else remains.
{
"id": 1234567
}
Report Created – Triggered when processing reaches a terminal state — a successful similarity score, index-only completion, or a processing failure. Check report.status rather than assuming success: this event fires for failures too.
{
"id": 1234567,
"filename": "Eigenvalues and Eigenvectors Problems 1.pdf",
"createdAt": "2026-06-08T08:19:18.094412Z",
"report": {
"status": "complete",
"score": 1,
"opened": false
},
"context": null,
"settings": {
"analyze": true,
"index": true
}
}
Report Updated – Triggered when a report's similarity score changes, either because sentences were excluded or restored, or because a parallel analysis updated the result. A document can produce several of these over its lifetime, so treat the latest as authoritative rather than accumulating changes.
{
"id": 1234567,
"filename": "image-20231017-154213.png",
"createdAt": "2026-06-09T12:22:14.237115Z",
"report": {
"status": "complete",
"score": 1,
"opened": true
},
"context": null,
"settings": {
"analyze": true,
"index": true
}
}
Report Deleted – Triggered when a report is cleared for reprocessing, resetting the document to an in-progress state. The document itself remains; report returns to null. Expect a document.report.created for the same document once reprocessing finishes.
{
"id": 1234567,
"filename": "Eigenvalues and Eigenvectors Problems 1.pdf",
"createdAt": "2026-06-08T08:19:18.094412Z",
"report": null,
"context": null,
"settings": {
"analyze": true,
"index": true
}
}
Troubleshooting
-
Signature never matches. Check that you are hex-encoding the digest instead of base64, or you are hashing a re-serialised body instead of the raw bytes.
- Events stopped arriving. Deliveries that fail for 16 hours are discarded permanently and are not resent when your endpoint recovers. Reconcile the gap through the API using the document IDs.
-
Duplicate events. Expected behaviour. Deduplicate on
X-Originality-Delivery-ID. -
Cannot tell which event arrived. Read
X-Originality-ResourceandX-Originality-Action. The body carries no event name.