Webhooks
Webhooks in WISEflow notify you when specific actions occur. By subscribing to an event, you can receive near real-time updates whenever that event happens in WISEflow. This makes it easier to keep data synchronized between systems and to monitor various aspects of the flow.
When an event within your WISEflow licence scope is detected, the webhook triggers and calls an external endpoint. The request payload contains enough information to handle the event directly or to retrieve more details via the WISEflow API. Each webhook is digitally signed, enabling you to verify the integrity of the data.
In this article:
Subscribing to a Webhook
You can subscribe to webhooks directly using the new endpoints available in the API. For details, refer to the Webhooks section in the API documentation.
Previously, subscribing to webhooks required contacting Customer Support to create a service task and receive a secret key for verification. This manual step is no longer necessary when using the new endpoints.
Setting up a webhook requires a POST callback URL.
Webhook Features
At-least-once Delivery
- The same message might be delivered more than once in instances with sporadic errors etc.
- The sequence number will be unique per event (e.g., per paper submitted). But not per webhook call
- All new webhook calls will have a unique ID
Security
- Data retention mechanisms: Calls will be deleted after 90 days
- Requires external endpoints to be HTTPS
- Unique ID to prevent replay attack
- Header with HMAC256 signature for body verification
Automatic retry of failed webhooks
- The webhook will call the external endpoint eight times with 15 minutes intervals
Verifying Webhook Signatures
When you configure a webhook, WISEflow generates a secret key that is used to sign all subsequent webhook requests. This allows you to verify that:
-
The webhook payload was sent by WISEflow
-
The payload has not been modified in transit
You must verify the webhook signature for every incoming request.
How signing works
Each webhook request includes the HTTP header:
X-WISEflow-Signature-256
In other words:
signature = Base64Encode(
HMAC_SHA256(secret_key, raw_request_body)
)
-
Read the raw request body exactly as received (before parsing JSON).
-
Compute the HMAC-SHA256 of the body using your secret key.
-
Base64-encode the resulting hash.
-
Compare the computed signature with the value from the
X-WISEflow-Signature-256header using a constant-time comparison.
Pseudocode
expected_signature = base64_encode(
hmac_sha256(secret, request_body)
)
verify(expected_signature == received_signature)
Python example
import hmac
import hashlib
import base64
def verify_signature(secret: str, received_signature: str, body_str: str) -> bool:
# Convert values to bytes
secret_bytes = secret.encode("utf-8")
body_bytes = body_str.encode("utf-8")
# Compute HMAC-SHA256
mac = hmac.new(secret_bytes, body_bytes, hashlib.sha256)
# Base64 encode the result
expected_signature = base64.b64encode(mac.digest()).decode("utf-8")
# Constant-time comparison to prevent timing attacks
return hmac.compare_digest(expected_signature, received_signature)
Important notes
-
Always use the raw request body when computing the signature. Any changes (such as re-serializing JSON) will invalidate the signature.
-
Use a constant-time comparison to avoid timing attacks.
-
If signature verification fails, the request should be rejected.
Available Webhooks
Paper Submitted
Will trigger when a paper has been submitted by a participant and if the participant withdraws the paper.
Depending on how the flow is set up, different actions will trigger the event:
- When a participant hands in a paper, an event will be triggered
- When a participant withdraws a paper, an event will be triggered
- When a participant hands in a group paper, one event will be triggered per group
- When all participants in a group hand in individual papers, one event will be triggered per group member
The event can be used to trigger additional actions, such as exporting submissions.
Payload:
{
"submissionId": 123,
"flowId": 123,
"userId": 123,
"participantId": 123,
"isHandedIn": true
}
Grade finalised
Will trigger when a grade has been finalised on a participant and if the finalised grade is revoked.
Depending on the marking process, different roles will trigger the event:
- In a double-blind marking process, the assessor will trigger the event
- In an approval by reviewer marking process, the reviewer will trigger the event
- In a section and/or rubric-based assessment process, the manager will trigger the event
- When a manager gives an administrative grade
- An event will also be triggered when the manager revokes a finalised grade
The event can be used to trigger additional actions, such as exporting submissions and assessments.
Payload:
{
"flowId": 12345,
"userId": 12345,
"submissionId": 12345,
"participantId": 12345,
"grade": 10,
"gradeDate": "2024-07-16T00:00:00Z",
"adminGrade": null,
"adminGradeDate": null
}
Alternative Grade Published
Will trigger when an alternative grade is published by an assessor or manager, as well as when a manager changes or removes an existing alternative grade.
The event can be used to trigger additional actions, such as exporting submissions and assessments.
Payload:
{
"flowId": 123,
"participantId": 123,
"grade": null,
"gradeDdate": null,
"adminGrade": "UA",
"adminGradeDate": "2024-07-16T00:00:00Z",
"action": "created",
"updatedBy": 12345,
"asRole": "manager"
}
Grade Justification
Will trigger when a participant requests a grade justification and when an assessor submits a grade justification response [Use case]
Payload:{
"flowId": 123,
"explanationRequestId": 123,
"participantId": 123,
"assessorId": 123
}
Assignment Added
Will trigger when an assignment is created, updated, added, or removed from a flow
Payload:
{
"assignmentId": 123,
"assignmentReference": "example",
"flowId": 123
}
Flow State Updated
Will trigger when the flow state is changing. E.g.: when the flow is activated or moves to the marking phase.
Payload:
{
"state": 1,
"flowId": 123
}
External Source Synchronisation
Will trigger when a manager requests a synchronisation on a flow set up with a Flow External Source
Payload:
{
"flowId": 123,
"externalSources": [
"123",
"ABC"
]
}
Webhooks Request Structure
The webhook header and body hold information on the current webhook call. The header and body are the same for all available webhooks.
Header
In the header you will find:
X-WISEflow-Signature-256
X-WISEflow-EventType
X-WISEflow-Event-Version
X-WISEflow-Trace-ID
X-WISEflow-CallTime
X-WISEflow-Delivery
X-WISEflow-Hook-Id
User-Agent, WISEflow
| Header | Description |
| Signature | A base64 encoded HMAC256 signature of the body. The HMAC256 signature is generated using a licence-specific secret. |
| Event-Type | The name of the webhook e.g. paper.submission or final.mark |
| Event-Version | The current version of the webhook |
| Trace-Id | The Trace-Id can be used to find matching entries in the log |
| Call-Time | The Call-Time when the call is made to the external API |
| Delivery | The Delivery is when the call is delivered to the external API |
| Hook Id | The webhook ID |
| User-Agent |
The user-agent that makes the webhook call |
Body
The body is a JSON object that describes the event that triggered the webhook.
In the body you will find:
{
"Id": "",
"action": "",
"event": "",
"licenseId": "",
"eventVersion": "",
"eventPayload": {
...
},
"callTime": "",
"eventVersion": "",
"sequenceNumber": "",
}
| Field | Format | Description |
| id | string | A unique ID generated by WISEflow to identify the event |
| action | string | Will be created, updated, or deleted depending on what has triggered the event. |
| event | string | The webhook you have subscribed to e.g., paper.submission or final.mark. |
| licenseId | integer | The ID of the licence that the event has been triggered from. This ID can be used if you have more than one WISEflow licence. |
| eventVersion | string | The same as the header above and is mainly used for debugging purposes. |
| eventPayload | The specific information received from the event | |
| callTime | integer | The time the call is made to the external API |
| eventVersion | string | The current version of the webhook |
| sequenceNumber | integer | The number that corresponds to the action and the event of the given call |