Webhook API
Table of Contents
- Introduction
- Usage
- Events for CodeSignal Pre-Screen
- Events for CodeSignal Pre-Screen Certify
- Pre-Screen Certify webhooks process flow
- certificationResultPending
- certificationResultNotCertified
- certificationResultShared
- certificationRequestExpired
- certificationRequestRejected
- certificationRequestsMerged
- Events for CodeSignal Live Interview
Introduction
What is a webhook?
A webhook is a way to receive an event notification from a server. When certain events occur in CodeSignal, a JSON payload containing information about the event will be sent via an HTTP POST request to a specified endpoint URL.
What information is needed to create a webhook?
- Endpoint URL: The URL that will receive POST payloads. This must be a valid URL that returns a 200 HTTP status after receiving each POST. When the webhook is created or updated, the URL will be POSTed with an empty payload, which must return a 200 status code within 10 seconds in order to successfully complete creating or updating the webhook.
- Event types: The list of events the webhook will be notified about.
- Secret key: (optional) A string used to generate a signature header that you can use to verify that the webhook data came from CodeSignal. The secret key is used in conjunction with the webhook's payload to generate a digital signature. Although it is not strictly required, we strongly encourage use of the secret key to verify that webhooks are actually coming from our platform.
- Owner emails: (optional) If the webhook cannot deliver its payload to the endpoint URL successfully, we will send a notification email to every provided email address. Read more about the retry policy below.
- Custom headers: (optional) Custom HTTP request headers that we will send to your endpoint in addition to the signature header.
How do I create a webhook?
If you're not already using CodeSignal, start a conversation with us by requesting a demo.
- Go to the Manage Webhooks page to create a webhook, and hit the "add a webhook" button.
- Fill out all of the required fields, as well as optional ones if you need them. We will generate a Secret key automatically, but you can overwrite it with a custom one.
- All newly created webhooks are initially active.
- A webhook that fails 25 times becomes "disabled".
- You can re-enable a webhook by updating its endpoint with a valid one.
Usage
Authentication
For webhooks with a secret key, CodeSignal will sign every request with a X-CodeSignal-Signature HTTP header, generated using the following algorithm:
getSignature(secretKey, endpointUrl, { eventType = '', triggeredOn = '' }) {
const plainText = `${endpointUrl}${eventType}${triggeredOn}`;
const hash = crypto.createHmac('sha256', secretKey);
hash.update(plainText);
return hash.digest('hex');
}
Here, triggeredOn is the timestamp present in every event payload.
Event handling
For each webhook, we store a list of events the hook should be notified about. When events have been triggered, CodeSignal sends webhooks one event at a time, at a rate of about one per second. If a web hook needs to be notified about multiple events, events are guaranteed to be sent in the order they were triggered.
Retry policy
secondsToWait = failCount^4 + 15 + (random(30) * (failCount + 1))
Assuming that random(30) always returns its average of 15, that would lead to the following retry attempts. (Not all retries are listed in the table, since this is just meant to give a sense of the retry backoff timeline.)
Previous failed attempts | Time since last attempt | Time since first attempt |
---|---|---|
0 | 30 seconds | 30 seconds |
1 | 46 seconds | 1.3 minutes |
2 | 1.2 minutes | 2.5 minutes |
3 | 2.6 minutes | 5.1 minutes |
5 | 12.2 minutes | 23 minutes |
10 | 2.8 hours | 7.4 hours |
13 | 8 hours | 1 day |
15 | 14 hours | 2.1 days |
24 | 3.8 days | 20.5 days |
CodeSignal will also send an email notification to the owner emails associated with the webhook, if any. To make sure we don't spam you, CodeSignal will send at most one email a day for every webhook. This means that you will generally be notified about the first fail, the 14th fail, and every fail starting from the 16th.
When a webhook is unhealthy, events will be queued in the order they were triggered, and the queue will not be flushed until the first event in the queue is able to be processed successfully (by receiving a 200 OK response from the endpoint URL). If you are able to identify and resolve the issue with the endpoint URL, you can open the webhook settings modal, then test and save the fixed webhook in order to mark it as healthy again. Once it is marked as healthy, the webhook will immediately flush its queue of pending events.
Webhook payloads
- event: [string] The type of event
- triggeredOn: [number] The timestamp of when the event was triggered, represented as number of milliseconds since the Unix epoch.
- payload: [object] The list of events the webhook will be notified about.
Events for CodeSignal Pre-Screen
Here is an overview of the end to end workflow of events for CodeSignal Pre-Screen.
Company Client workflow
companyTestSessionCreated
Fired when a test session is created.
{
event: 'companyTestSessionCreated',
triggeredOn: number,
payload: {
testSessionId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string
}
};
{
event: 'companyTestSessionCreated',
triggeredOn: 1553720789347,
payload: {
testSessionId: 'lehu382hdleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'Software Engineer intern',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe'
}
};
Candidate workflow
preScreenCandidateDeclined
Fired when a candidate declines a test session invitation.
{
event: 'preScreenCandidateDeclined',
triggeredOn: number,
payload: {
testSessionId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
externalId?: string
}
};
{
event: 'preScreenCandidateDeclined',
triggeredOn: 1553720789347,
payload: {
testSessionId: 'lehu382hdleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'Software Engineer intern',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe'
}
};
preScreenResultShared
Fired when a candidate shares their existing result with a test session invitation.
{
event: 'preScreenResultShared',
triggeredOn: number,
payload: {
testSessionId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
duration: number,
score: number,
maxScore: number | null | undefined,
plagiarismLevel: number,
plagiarismLabel: 'none' | 'low' | 'medium' | 'high' | '-',
url: string,
codingScore?: number | null, // deprecated; use versionedCodingScore instead
versionedCodingScore?: {
version: 'original' | 'codingScore2023',
value: number,
},
externalId?: string
}
};
{
event: 'preScreenResultShared',
triggeredOn: 1553720789347,
payload: {
testSessionId: 'lehu382hdleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'Software Engineer intern',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
duration: 3480000, // 58 minutes
score: 900,
maxScore: 1000,
codingScore: 820,
versionedCodingScore: {
version: 'codingScore2023',
value: 575
},
plagiarismLevel: 0.5,
plagiarismLabel: 'medium',
url: https://app.codesignal.com/test-result/oH3qeBC38oFsf7qB4?accessToken=A7HnBpab4aD7xm7Kp-iPhk2se5Wnxeg7x9GruANLzn
}
};
preScreenExpired
Fired when a test session invitation cannot be fulfilled within the expiration window. The expiredReason field indicates whether the expiration is due to the test not being taken at all, or whether the test could not be certified by CodeSignal due to possibility/suspicion of cheating. If the latter, a rejectedReasons field will also be provided.
{
event: 'preScreenExpired',
triggeredOn: number,
payload: {
testSessionId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
externalId?: string,
expiredReason: 'testNotTaken' | 'notCertified',
rejectedReasons?: Array<string>,
}
};
{
event: 'preScreenExpired',
triggeredOn: 1553720789347,
payload: {
testSessionId: 'lehu382hdleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'Software Engineer intern',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
expiredReason: 'notCertified',
rejectedReasons: ['Unauthorized resource', 'Presence of others'],
}
};
companyTestSessionStarted
Fired when a candidate starts a test session.
{
event: 'companyTestSessionStarted',
triggeredOn: number,
payload: {
testSessionId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string
}
};
{
event: 'companyTestSessionStarted',
triggeredOn: 1553720789347,
payload: {
testSessionId: 'lehu382hdleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'Software Engineer intern',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe'
}
};
preScreenResultVerificationPending
Fired when a candidate finishes a proctored test session.
{
event: 'preScreenResultVerificationPending',
triggeredOn: number,
payload: {
testSessionId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
duration: number,
score: number,
maxScore: number | null | undefined,
codingScore?: number | null, // deprecated; use versionedCodingScore instead
versionedCodingScore?: {
version: 'original' | 'codingScore2023',
value: number,
},
externalId?: string
}
};
{
event: 'preScreenResultVerificationPending',
triggeredOn: 1553720789347,
payload: {
testSessionId: 'lehu382hdleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'Software Engineer intern',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
duration: 3480000, // 58 minutes
score: 900,
maxScore: 1000,
codingScore: 820,
versionedCodingScore: {
version: 'codingScore2023',
value: 575
},
}
};
companyTestSessionFinished
Fired when a non-proctored test session is finished.
{
event: 'companyTestSessionFinished',
triggeredOn: number,
payload: {
testSessionId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
duration: number, // milliseconds
score: number,
maxScore: number,
codingScore?: number | null, // deprecated; use versionedCodingScore instead
versionedCodingScore?: {
version: 'original' | 'codingScore2023',
value: number,
},
plagiarismLevel: number,
plagiarismLabel: 'none' | 'low' | 'medium' | 'high' | '-',
url: string,
}
};
{
event: 'companyTestSessionFinished',
triggeredOn: 1553720789347,
payload: {
testSessionId: 'lehu382hdleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'Software Engineer intern',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
duration: 3480000, // 58 minutes
score: 900,
maxScore: 1000,
codingScore: 820,
versionedCodingScore: {
version: 'codingScore2023',
value: 575
},
plagiarismLevel: 0.5,
plagiarismLabel: 'medium',
url: https://app.codesignal.com/test-result/oH3qeBC38oFsf7qB4?accessToken=A7HnBpab4aD7xm7Kp-iPhk2se5Wnxeg7x9GruANLzn
}
};
preScreenIntegrityReviewUpdated
Fired when a non-proctored test session's integrity review status is updated.
{
event: 'preScreenIntegrityReviewUpdated',
triggeredOn: number,
payload: {
testSessionId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
integrityReviewSuggested: boolean,
}
};
{
event: 'preScreenIntegrityReviewUpdated',
triggeredOn: 1553720789347,
payload: {
testSessionId: 'lehu382hdleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'Software Engineer intern',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
integrityReviewSuggested: true
}
};
CodeSignal Auditor workflow
preScreenResultNotVerified
Fired when a test session is marked as not verified by CodeSignal. This means that either the session has been flagged for suspicious behavior, or a technical issue has been identified during the session (in which case the candidate will be automatically granted a retake).
{
event: 'preScreenResultNotVerified',
triggeredOn: number,
payload: {
testSessionId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
rejectedReasons?: Array<string>,
}
};
{
event: 'preScreenResultNotVerified',
triggeredOn: 1553720789347,
payload: {
testSessionId: 'lehu382hdleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'Software Engineer intern',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
rejectedReasons: ['Incomplete recording', 'Screen not visible],
}
};
preScreenResultVerified
Fired when a proctored test session is verified by CodeSignal.
{
event: 'preScreenResultVerified',
triggeredOn: number,
payload: {
testSessionId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
duration: number, // milliseconds
score: number,
maxScore: number,
codingScore?: number | null, // deprecated; use versionedCodingScore instead
versionedCodingScore?: {
version: 'original' | 'codingScore2023',
value: number,
},
plagiarismLevel: number,
plagiarismLabel: 'none' | 'low' | 'medium' | 'high' | '-',
url: string,
}
};
{
event: 'preScreenResultVerified',
triggeredOn: 1553720789347,
payload: {
testSessionId: 'lehu382hdleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'Software Engineer intern',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
duration: 3480000, // 58 minutes
score: 900,
maxScore: 1000,
codingScore: 820,
versionedCodingScore: {
version: 'codingScore2023',
value: 575
},
plagiarismLevel: 0.5,
plagiarismLabel: 'medium',
url: https://app.codesignal.com/test-result/oH3qeBC38oFsf7qB4?accessToken=A7HnBpab4aD7xm7Kp-iPhk2se5Wnxeg7x9GruANLzn
}
};
Events for CodeSignal Pre-Screen Certify
Pre-Screen Certify webhooks process flow
certificationResultPending
Fired when a candidate finishes a standardized test session, if that session has been autoshared with your company. The certification request is still pending and has not yet been certified by the CodeSignal certification team.
{
event: 'certificationResultPending',
triggeredOn: number,
payload: {
certificationRequestId: string,
testSessionId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
duration: number, // milliseconds
score: number,
maxScore: number,
codingScore: number,
}
};
{
event: 'certificationResultPending',
triggeredOn: 1553720789347,
payload: {
certificationRequestId: 'lehjeh36dleh29',
testSessionId: '2TgbrRMkBiksAXqEd',
testId: 'dhey29hcl28hl28',
testTitle: 'General Coding Assessment',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
duration: 5286253,
score: 876,
maxScore: 1300,
codingScore: 793
}
};
certificationResultNotCertified
Fired when a candidate's standardized test session is denied certification by the CodeSignal certification team, if that test session has been autoshared with your company. The certification request will remain open even after this, in case of retakes or technical issues, so receiving this webhook is not "final" -- you may subsequently receive a certificationResultShared or certificationRequestExpired event.
{
event: 'certificationResultNotCertified',
triggeredOn: number,
payload: {
certificationRequestId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
rejectedReasons: Array<string>,
}
};
{
event: 'certificationResultNotCertified',
triggeredOn: 1553720789347,
payload: {
certificationRequestId: 'lehjeh36dleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'General Coding Assessment',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
rejectedReasons: ['Unauthorized resource', 'Presence of others'],
}
};
certificationResultShared
Fired when a candidate shares their certification result(s) to a specific certification request, or when the result they agreed to automatically share gets certified.
{
event: 'certificationResultShared',
triggeredOn: number,
payload: {
certificationRequestId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
sharedTestSessions: Array<{ // ordered by codingScore in descending order
codingScore: number,
score: number,
maxScore: number,
startDate: number,
finishDate: number,
duration: number,
id: string,
url: string,
}>
}
};
{
event: 'certificationResultShared',
triggeredOn: 1553720789347,
payload: {
certificationRequestId: 'lehjeh36dleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'General Coding Assessment',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
sharedTestSessions: [{
codingScore: 823,
score: 1148,
maxScore: 1300,
startDate: 1618504200000,
finishDate: 1618507800000,
duration: 3600000,
id: '272bu5Na997EdzmQ8',
url: https://app.codesignal.com/coding-report/8rRHAnRb3mW6RSfJG-a7RFoj2CESoWCji588zCnDJy/J3y6nqx6C89oMq998?accessToken=5ejNbPLLD79xFGNuQ-cEq43zQPQrQqkg2cn3danTag,
}, {
codingScore: 793,
score: 876,
maxScore: 1300,
startDate: 1619195400000,
finishDate: 1619199000000,
duration: 3600000,
id: '2TgbrRMkBiksAXqEd',
url: https://app.codesignal.com/coding-report/8rRHAnRb3mW6RSfJG-a7RFoj2CESoWCji588zCnDJy/J3y6nqx6C89oMq998?accessToken=5ejNbPLLD79xFGNuQ-cEq43zQPQrQqkg2cn3danTag,
}]
}
};
certificationRequestExpired
Fired when a request for a certified test result cannot be fulfilled within the expiration window. The expiredReason field indicates whether the expiration is due to the test not being taken at all, or whether the test could not be certified by CodeSignal due to possibility/suspicion of cheating. If the latter, a rejectedReasons field will also be provided.
{
event: 'certificationRequestExpired',
triggeredOn: number,
payload: {
certificationRequestId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
expiredReason: 'testNotTaken' | 'notCertified',
rejectedReasons?: Array<string>,
}
};
{
event: 'certificationRequestExpired',
triggeredOn: 1553720789347,
payload: {
certificationRequestId: 'lehjeh36dleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'General Coding Assessment',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
expiredReason: 'notCertified',
rejectedReasons: ['Unauthorized resource', 'Presence of others'],
}
};
certificationRequestRejected
Fired when a candidate explicitly declines a request to share a certification result. This means that they neither took the test upon request nor agreed to share any pre-existing results.
{
event: 'certificationRequestRejected',
triggeredOn: number,
payload: {
certificationRequestId: string,
testId: string,
testTitle: string,
candidateEmail: string,
candidateName: string,
}
};
{
event: 'certificationRequestRejected',
triggeredOn: 1553720789347,
payload: {
certificationRequestId: 'lehjeh36dleh29',
testId: 'dhey29hcl28hl28',
testTitle: 'General Coding Assessment',
candidateEmail: 'jane.doe@gmail.com',
candidateName: 'Jane Doe',
}
};
certificationRequestsMerged
This event is fired in case two Certification Requests get merged together. This can happen if a candidate gets invited to take the same assessment using different email addresses. In this case the newer request will be deleted, and its data will be transferred to the older one.
{
event: 'certificationRequestsMerged',
triggeredOn: number,
payload: {
deletedRequestId: string,
mergedRequestId: string,
}
};
{
event: 'certificationRequestRejected',
triggeredOn: 1553720789347,
payload: {
deletedRequestId: 'lehjeh36dleh29',
mergedRequestId: 'dhey29hcl28hl28',
}
};
Events for CodeSignal Live Interview
Live Interview webhooks process flow
liveInterviewFinished
Fired when an interviewer finishes a live interview or when a live interview times out.
{
event: 'liveInterviewFinished',
triggeredOn: number,
payload: {
interviewId: string,
candidateName: string,
candidateEmail: string,
startDate: number,
endDate: number,
questionsSolved: number,
questionsAttempted: number,
url: string,
}
};
{
event: 'liveInterviewFinished',
triggeredOn: 1553720789347,
payload: {
interviewId: 'zWve8sgiLJit6isiM',
candidateName: 'Jane Doe',
candidateEmail: 'jane.doe@gmail.com',
startDate: 1727896812565,
endDate: 1727896849868,
questionsSolved: 1,
questionsAttempted: 2,
url: https://app.codesignal.com/client-dashboard/interviews?status=over&liveInterviewId=zWve8sgiLJit6isiM
}
};
liveInterviewFeedbackUpdated
Fired when an interviewer adds or updates interview feedback.
{
event: 'liveInterviewFeedbackUpdated',
triggeredOn: number,
payload: {
interviewId: string,
interviewerId: string,
feedback: {
generalNotes: string,
categories?: Array<{
name: string,
attributes: Array<{
name: string,
score?: number,
notes?: string,
}>,
}>,
recommendedLevel?: string,
overallEvaluation?: string,
}
}
};
{
event: 'liveInterviewFeedbackUpdated',
triggeredOn: 1553720789347,
payload: {
interviewId: 'zWve8sgiLJit6isiM',
interviewerId: 'qcmbuCuxpHELGYEJw',
feedback: {
generalNotes: 'Great candidate, very knowledgeable',
categories: [
{
name: 'Problem Solving',
attributes: [
{
name: 'Problem Solving',
score: 4,
notes: 'Great problem solving skills',
},
{
name: 'Code Quality',
score: 3,
},
],
},
{
name: 'Communication',
attributes: [
{
name: 'Communication',
score: 5,
notes: 'Excellent communication skills',
},
],
},
],
recommendedLevel: 'Senior',
overallEvaluation: 'Strong Yes',
}
}
};