Verification results webhook
When a client completes the verification - your registered webhook endpoint(you can register webhook endpoint in the dashboard) will receive a HTTP POST request containing data and status of the verification.
Your API must return HTTP response with status code 200
. Otherwise, a client will see a failed verification message and will be redirected to a URL of a failed verification.
If you are experiencing any issues receiving a webhook - webhook troubleshooting section.
All of our webhooks are TLS encrypted and require a valid SSL certificate of your webhook endpoint, otherwise, the webhook sending will fail.
Verification and webhook flow
When to expect a webhook
There are certain points of time in identity verification process when your API should expect to receive a webhook.
Your API should expect to receive a webhook within 20 minutes when:
- A client has successfully completed a verification
- A client has consecutively failed to identify himself and has reached maximum reattempts allowed in the verification platform.
- A client was suspected for a fraudulent activity and his session was terminated.
Your API should not expect to receive within 20 minutes callback when (In case of these events your API will receive a callback only when a token (token property tokenExpiry
) or a token session (token property sessionLength
) has expired. For more on these variables refer to generating verification token):
- A client has consecutively failed to identify himself and has not reached maximum reattempts allowed in the verification platform.
- A client did not start a verification session.
Default callback
Default callback is represented in a UML activity graph below:
- Generate a token by sending an HTTP POST request to
https://ivs.idenfy.com/api/v2/token
endpoint. The request must contain basic auth headers, where the username is the API key and the password is API secret.
More information can be found here.
- Redirect client to iDenfy platform. It can be done in several ways:
- Client redirect to verification WEB platform
- Client redirect to verification WEB platform (iFrame)
- iOS SDK
- Android SDK
- Client submits all documents (tries to verify at least one time).
3.1. If a client doesn't upload all photos at least one time, for example, face photos are missing and only the document’s photo are captured after session time expires iDenfy system will send a callback about expired notification to the provided URL with overall status: ”EXPIRED”.
- The client completes the verification process. When the verification process is complete, please check here.
4.1. If a client captures all photos at least one time, but verification was unsuccessful while more attempts are available after session time expires iDenfy system will send a callback about expired notification to the provided URL with overall status: ”DENIED”.
- After the verification process is completed or 4.1 callback sent we will perform full document analysis by automated system and human supervision. The analysis process may take up to 20 minutes, with an average duration of about 10 minutes.
- After the analysis process is done, we are sending all data to the registered callback URL.
- Receiving all data sent by iDenfy to your provided endpoint.
- Returning HTTP status code 200.
8.1. HTTP status code is not 200. Trying to resend callback (max 3 times) every 0,5 seconds.
How many webhooks to expect: If You get a 3.1 callback with overall status EXPIRED it would be just one callback.
If a client does not complete verification and 4.1 step occurs, the system will send a DENIED callback and after automated system and human supervision analysis, we will send the final callback.
In default flow (steps 1, 2, 3, 4, 5, 6, 7) You would get only one callback with final results after human supervision analysis.
Webhooks with auto callback
Callback with automatic callback are represented in a UML activity graph below:
- Generate a token by sending an HTTP POST request to
https://ivs.idenfy.com/api/v2/token
endpoint. The request must contain basic auth headers, where the username is the API key and the password is API secret.
More information can be found here.
- Redirect the client to the iDenfy platform. It can be done in several ways:
- Client redirect to verification WEB platform
- Client redirect to verification WEB platform (iFrame)
- iOS SDK
- Android SDK
- Client submits all documents (tries to verify at least one time).
3.1. If a client doesn't upload all photos at least one time, for example, face photos are missing and only the document’s photo are captured after session time expires iDenfy system will send a callback about expired notification to the provided URL with overall status: ”EXPIRED”.
- The client completes the verification process. When the verification process is complete, please check here.
4.1. If a client captures all photos at least one time, but verification was unsuccessful while more attempts are available after session time expires iDenfy system will send a callback about expired notification to the provided URL with overall status: ”DENIED”.
- After the verification process is completed we will perform full document analysis by the automated system.
- After the automatic analysis process is done, we are sending all data with auto results to the registered callback URL.
- Receiving all data with auto results sent by iDenfy to your provided endpoint.
- Returning HTTP status code 200.
8.1. HTTP status code is not 200. Trying to resend callback (max 3 times) every 0,5 seconds.
- After the automatic verification process is completed and 200 status received we will perform full document analysis by human supervision. The analysis process may take up to 20 minutes, with an average duration of about 10 minutes.
- After the manual analysis process is done, we are sending all data with manual results to the registered callback URL.
- Receiving all data with manual results sent by iDenfy to your provided endpoint.
- Returning HTTP status code 200.
12.1. HTTP status code is not 200. Trying to resend callback (max 3 times) every 0,5 seconds.
You should expect two webhooks:
- Callback after automatic analysis
- Callback after manual review
If case 3.1 occurs You should expect to get only one callback: Callback about an expired session
Callback structure
Request HTTP body is in JSON format which is described in tables below:
JSON keys could be added or changed in the future.
JSON key | Type | Constraints | Explanation |
---|---|---|---|
final | Bool | - | If true , this callback is likely the final callback for the verification (unless it will be repeatedly manually reviewed). If false and there is ID_VERIFICATION and/or ID_VERIFICATION_MANUAL_FINISHED webhook setup at notifications then expect to receive another callback/webhook for that verification, when it is reviewed manually. |
platform | String | - Max length 30 | Tells from which device (platform) a client completed a verification. Possible values: - PC - MOBILE - TABLET - MOBILE_APP - MOBILE_SDK - OTHER |
status | Object | - | Dictionary that contains the status of the verification. Refer to status table. |
data | Object | - | Dictionary that contains parsed data from clients identity document. Refer to data table. |
fileUrls | Object | - | Dictionary that contains url links to download or view client's verification photos and videos. Refer to file urls table |
AML | List | - | List of objects that contains anti-money-laundering (AML) service data. Only applicable if AML is enabled for you. Refer to AML documentation. |
LID | List | - | List of objects that contains lost-invalid-documents (LID) service data. Only applicable if LID is enabled for you. Refer to LID documentation. |
scanRef | String | - Max length 36 | A unique string to trace back a verification on iDenfy’s side. |
externalRef | String | - Max length 40 | A unique string for external reference to link better the client to you and the iDenfy system. |
clientId | String | - Max length 100 | A unique string to trace back a client on your side. |
startTime | Int | - | A timestamp of when a client starts the verification process. |
finishTime | Int | - | A timestamp of when the final decision for automatic processing was made. |
clientIp | String | - Max length 39 | A string of client's system IP address. |
clientIpCountry | String | - Country alpha-2 code | A country code of the client's system IP address. |
clientLocation | String | - Max length 100 | Full information available about the client's current location based on the IP address. |
manualAddress | String | - | Client address parsed from utility bill. Only occurs if utility bill was uploaded. |
manualAddressMatch | Bool | - | Indicates if the document's address matched with the address provided by the partner after the manual check. |
additionalStepPdfUrls | Object | - | Contains a URL "UTILITY_BILL": "https://..." to download the user's uploaded PDF document from additional verification step |
addressVerification | JSON object | - | Client's address verification. If this feature isn't enabled or this is not the final callback with manualAddressMatch status true - this field will be null |
registryCenterCheck | JSON object | - | Client's registry center check. If this feature isn't enabled - this field will be null |
questionnaireAnswers | JSON object | - | Provided questionnaire answers. If this feature isn't enabled and/or the questionnaire was not added to verification - this field will be null |
companyId | String | - | Company ID of KYB form/company that this verification is related to. Expect null if verification is not related with any company/form |
beneficiaryId | String | - | Beneficiary ID of KYB form/company that this verification is related to. Expect null if verification is not related with any company/form |
additionalSteps | JSON object | - | An object in which custom additional step name and type/method, that was used for verification, is returned. Expect null if verification has no custom additional steps. |
riskAssessment | JSON object | - | An object in which Risk Assessment score (risk_score ) and level (risk_level ) is returned, if RA profile was used for verification. |
Verification status table
JSON key | Type | Constraints | Explanation |
---|---|---|---|
overall | String | - Max length 30 | An overall status of the verification. It is a combination of manual and automatic verification results. Possible values: - APPROVED - DENIED - SUSPECTED * - EXPIRED * Overall status of the verification. It depends on auto or manual webhook, but if face status: autoFace , manualFace is FACE_MATCH and document status autoDocument , manualDocument is DOC_VALIDATED , then the verification is approved. If other statuses exist, for example: DOC_NOT_FOUND , FACE_MISMATCH , then verification is declined. If any fraudTags or mismatchTags exist for the verification, then status can be set as SUSPECTED , because the identification caught some discrepancies. If verification is approved(FACE_MATCH and DOC_VALIDATED ) but overall status SUSPECTED it should be decided on your end to allow or decline this client. For value explanations refer to status vocabulary |
fraudTags | List | - | A list of fraud tags (strings) indicating why verification was SUSPECTED . Possible values: - FACE_SUSPECTED - FACE_BLACKLISTED - DOC_FACE_BLACKLISTED - DOC_MOBILE_PHOTO - DEV_TOOLS_OPENED - DOC_PRINT_SPOOFED - FAKE_PHOTO - AML_SUSPECTION - AML_FAILED - LID_SUSPECTION - LID_FAILED - RC_FAILED - UTILITY_ADDRESS_CHECK_FAILURE - VIRTUAL_CAMERA - FACE_IN_BLACKLIST - DOC_FACE_IN_BLACKLIST - DUPLICATE_FACE - DUPLICATE_DOC_FACE For value explanations refer to status vocabulary. |
mismatchTags | List | - | A list of mismatch tags (strings) indicating where partner provided document information does not match with the read information. Verification overall status was SUSPECTED if any mismatchTags exist. Possible values: -NAME -SURNAME -DOCUMENT_NUMBER -PERSONAL_CODE -EXPIRY_DATE -DATE_OF_BIRTH -DATE_OF_ISSUE -FULL_NAME -UNDER_AGE -UNKNOWN_AGE -SEX -NATIONALITY -INVALID_ADDITIONAL_STEP -ADDITIONAL_STEP_NOT_FOUND -DOC_INFO_MISMATCH -ADDITIONAL_STEP_INFORMATION_MISMATCH -EXPIRED_ADDITIONAL_STEP_INFORMATION -REGISTRY_CENTER_INFO_MISMATCH For value explanations refer to status vocabulary. |
autoFace | String | - Max length 30 | An automatic face analysis result (decision made by an automated platform). Possible values: - FACE_MATCH - FACE_MISMATCH - NO_FACE_FOUND - TOO_MANY_FACES - FACE_TOO_BLURRY - FACE_GLARED - FACE_UNCERTAIN - FACE_NOT_ANALYSED - FACE_NOT_CHECKED - FACE_ERROR - AUTO_UNVERIFIABLE - FAKE_FACE - null For value explanations refer to status vocabulary. |
manualFace | String | - Max length 30 | A Final face analysis result (decision made by a automatic system and human). Possible values: - FACE_MATCH - FACE_MISMATCH - NO_FACE_FOUND - TOO_MANY_FACES - FACE_UNCERTAIN - FAKE_FACE For value explanations refer to status vocabulary. |
autoDocument | String | - Max length 30 | An automatic document analysis result (decision made by an automated platform). Possible values: - DOC_VALIDATED - DOC_INFO_MISMATCH - DOC_NOT_FOUND - DOC_NOT_FULLY_VISIBLE - DOC_NOT_SUPPORTED - DOC_FACE_NOT_FOUND - DOC_TOO_BLURRY - DOC_GLARED - DOC_FACE_GLARED - MRZ_NOT_FOUND - MRZ_OCR_READING_ERROR - BARCODE_NOT_FOUND - DOC_EXPIRED - COUNTRY_MISMATCH - DOC_TYPE_MISMATCH - DOC_SIDE_MISMATCH - DOC_DAMAGED - DOC_FAKE - DOC_ERROR - AUTO_UNVERIFIABLE - DOC_NOT_ANALYSED - DOC_NAME_ERROR - DOC_SURNAME_ERROR - DOC_EXPIRY_ERROR - DOC_DOB_ERROR - DOC_PERSONAL_NUMBER_ERROR - DOC_NUMBER_ERROR - DOC_DATE_OF_ISSUE_ERROR - DOC_SEX_ERROR - DOC_NATIONALITY_ERROR - COUNTRY_NOT_SUPPORTED - DOC_PERSONAL_CODE_INVALID - DOC_SPOOF_DETECTED - MRZ_INVALID - DOC_NOT_ALLOWED For value explanations refer to status vocabulary. |
manualDocument | String | - Max length 30 | A Final document analysis result (decision made by a automatic system and human). Possible values: - DOC_VALIDATED - DOC_NOT_FULLY_VISIBLE - DOC_NOT_SUPPORTED - DOC_EXPIRED - DOC_DAMAGED - DOC_FAKE - DOC_PERSONAL_CODE_INVALID - MRZ_INVALID For value explanations refer to status vocabulary. |
additionalSteps | String | - Max length 30 | An additional steps result (decision made by human). Possible values: - VALID - INVALID - NOT_FOUND |
Face and document statuses after manual review table
JSON key | Type | Constraints | Explanation |
---|---|---|---|
manualFace | String | - Max length 30 | A Final face analysis result (decision made by a automatic system and human). Possible values: - FACE_MATCH - FACE_MISMATCH - NO_FACE_FOUND - TOO_MANY_FACES - FACE_UNCERTAIN - FAKE_FACE For value explanations refer to status vocabulary. |
manualDocument | String | - Max length 30 | A Final document analysis result (decision made by a automatic system and human). Possible values: - DOC_VALIDATED - DOC_NOT_FULLY_VISIBLE - DOC_NOT_SUPPORTED - DOC_EXPIRED - DOC_DAMAGED - DOC_FAKE - DOC_PERSONAL_CODE_INVALID - MRZ_INVALID For value explanations refer to status vocabulary. |
Data table
- Any of the specified fields below can be
null
. - Some fields in original language could have symbols encoded in UTF-16.
JSON key | Type | Constraints | Explanation |
---|---|---|---|
docFirstName | String | - Max length 100 | Clients name parsed from the document. |
docLastName | String | - Max length 100 | Clients surname parsed from the document. |
docNumber | String | - Max length 100 | Clients document number parsed from the document. |
docPersonalCode | String | - Max length 15 | Clients personal code parsed from the document. |
docExpiry | String | - Max length 100 | Clients document expiry date parsed from the document. |
docDob | String | - Max length 100 | Clients date of birth parsed from the document. |
docDateOfIssue | String | - Max length 100 | Clients date of issue parsed from the document. |
docType | String | - Max length 30 | Clients used document type to complete verification. Possible values: - ID_CARD - PASSPORT - RESIDENCE_PERMIT - DRIVER_LICENSE - PAN_CARD - AADHAAR - OTHER - VISA - BORDER_CROSSING - ASYLUM - NATIONAL_PASSPORT - PROVISIONAL_DRIVER_LICENSE - VOTER_CARD - OLD_ID_CARD - TRAVEL_CARD - PHOTO_CARD - MILITARY_CARD - PROOF_OF_AGE_CARD - DIPLOMATIC_ID |
docSex | String | - Max length 9 | Clients sex parsed from the document. Possible values: - MALE - FEMALE - UNDEFINED |
docNationality | String | - Country alpha-2 code | Clients nationality parsed from the document. |
docIssuingCountry | String | - Country alpha-2 code | Clients documents issuing country parsed from the document. |
docTemporaryAddress | String | - Max length 100 | Clients temporary address parsed from the document. |
docBirthName | String | - Max length 100 | Clients birth name parsed from the document. |
docPatronymic | String | - Max length 100 | Clients father's name parsed from the document. |
birthPlace | String | - Max length 60 | Clients birth place parsed from the document. |
authority | String | - Max length 60 | The authority of the document parsed from the document. |
address | String | - Max length 100 | Clients address parsed from the document. |
mothersMaidenName | String | - Max length 80 | Clients mothers maiden name parsed from the document. |
driverLicenseCategory | String | - Max length 30 | Clients driving license categories (classes) parsed from the document. |
manuallyDataChanged | Bool | - | Indicates whether a manual reviewer has changed any parsed data from the document. Applicable only when a human reviews a verification. In automated verification response this value is false . |
fullName | String | - Max length 201 | Clients full name parsed from the document. |
selectedCountry | String | - Any country in alpha-2 code | Country which was selected in verification process. |
orgFirstName | String | - Max length 255 | Client name parsed from the document in original language. |
orgLastName | String | - Max length 255 | Client surname parsed from the document in original language. |
orgNationality | String | - Max length 255 | Client nationality parsed from the document in original language. |
orgBirthPlace | String | - Max length 500 | Client birth place parsed from the document in original language. |
orgAuthority | String | - Max length 500 | Client document authority categories parsed from the document in original language. |
orgAddress | String | - Max length 500 | Client address parsed from the document in original language. |
orgTemporaryAddress | String | - Max length 500 | Client temporary address parsed from the document in original language. |
orgMothersMaidenName | String | - Max length 500 | Clients mothers maiden name parsed from the document in original language. |
orgPatronymic | String | - Max length 500 | Clients father's name parsed from the document in original language. |
orgBirthName | String | - Max length 500 | Client birth name parsed from the document in original language. |
ageEstimate | String | - Max length 10 | Client age detected from selfie when liveness is on. Possible values: - UNDER_13 - OVER_13 - OVER_18 - OVER_22 - OVER_25 - OVER_30 |
clientIpProxyRiskLevel | String | - Max length 11 | Client's IP address proxy risk level. If this feature isn't enabled or this is not the final callback with overall status APPROVED - this field will be null . Possible values: - VERY_LOW - LOW - MEDIUM - HIGH - VERY_HIGH - NOT_CHECKED |
duplicateFaces | List | - | Indicates scanRefs of other clients which selfie faces were matched with this one's verification. If list is empty, a value is null . |
duplicateDocFaces | List | - | Indicates scanRefs of other clients which document faces were matched with this one's verification. If list is empty, a value is null . |
additionalData | JSON object | Must be used with additionalSteps | Additional data provided alongside any additionalSteps, for example - Social Security Number in UTILITY_BILL . Must be used with additionalSteps |
File URLs table
There's an expiration time for all below-provided URLs, which is 3600 seconds (1h) after they are created. In order to receive active links, you should initiate new webhook via dashboard or use any data retrieval endpoints, for example, verification file URLs.
JSON key | Type | Can be null | Constraints | Explanation |
---|---|---|---|---|
FRONT | String (URL) | Yes | - Max length 500 | A URL to download front document side photo with which a client has completed a verification. |
BACK | String (URL) | Yes | - Max length 500 | A URL to download back document side photo with which a client has completed a verification. |
FACE | String (URL) | Yes | - Max length 500 | A URL to download face photo with which a client has completed a verification. |
FRONT_VIDEO | String (URL) | Yes | - Max length 500 | A URL to download the video of a client taking the front photo. |
BACK_VIDEO | String (URL) | Yes | - Max length 500 | A URL to download the video of a client taking the back photo. |
FACE_VIDEO | String (URL) | Yes | - Max length 500 | A URL to download the video of a client taking the face photo. |
ADDITIONAL_DOCUMENT | String (URL) | Yes | - Max length 500 | A URL to download additional document front side photo. |
ADDITIONAL_DOCUMENT_BACKSIDE | String (URL) | Yes | - Max length 500 | A URL to download additional document backside side photo. |
ADDITIONAL_DOCUMENT_VIDEO | String (URL) | Yes | - Max length 500 | A URL to download the video of a client taking the additional document front side photo. |
ADDITIONAL_DOCUMENT_BACKSIDE_VIDEO | String (URL) | Yes | - Max length 500 | A URL to download the video of a client taking the additional document backside side photo. |
Examples
This is an example JSON body in the callback HTTP request.
{
"final": true,
"platform": "PC",
"status": {
"overall": "APPROVED",
"suspicionReasons": [],
"denyReasons": [],
"fraudTags": [],
"mismatchTags": [],
"autoFace": "FACE_MATCH",
"manualFace": "FACE_MATCH",
"autoDocument": "DOC_VALIDATED",
"manualDocument": "DOC_VALIDATED",
"additionalSteps": "NOT_FOUND",
"amlResultClass": null,
"pepsStatus": null,
"sanctionsStatus": null,
"adverseMediaStatus": null
},
"data": {
"docFirstName": "MANFRED",
"docLastName": "WEBER",
"docNumber": "DE4878783",
"docPersonalCode": null,
"docExpiry": "2024-03-09",
"docDob": "1972-07-10",
"docDateOfIssue": "2014-03-09",
"docType": "PASSPORT",
"docSex": "MALE",
"docNationality": "NL",
"docIssuingCountry": "NL",
"birthPlace": "LONDON",
"authority": "BURG",
"address": null,
"docTemporaryAddress": null,
"mothersMaidenName": null,
"docBirthName": null,
"driverLicenseCategory": null,
"manuallyDataChanged": false,
"fullName": "MANFRED WEBER",
"selectedCountry": "NL",
"orgFirstName": "MANFRED",
"orgLastName": "WEBER",
"orgNationality": "NEDERLANDSE",
"orgBirthPlace": "LONDON",
"orgAuthority": "BURG",
"orgAddress": null,
"orgTemporaryAddress": null,
"orgMothersMaidenName": null,
"orgBirthName": null,
"ageEstimate": null,
"clientIpProxyRiskLevel": "VERY_LOW",
"duplicateFaces": null,
"duplicateDocFaces": null,
"additionalData": {
"UTILITY_BILL": {
"address": {
"status": "NO_DATA"
}
}
}
},
"fileUrls": {
"FACE": "https://s3.eu-west-1.amazonaws.com/production.users.storage/users_storage/users/eW_Dl8KdwpLCtsKlwqtywrHChcOXwrp0wqd4wp7DmsKXwpPCtMKpwqpywqnCrcKkwoZ4wqd4csKqwqBmwrU%3D/FACE.png?AWSAccessKeyId=AKIAJEE33B4FZLU73WMA&Signature=FLupfDktZyB4vbhYsSStIPvHV0o%3D&Expires=1700739964",
"FRONT": "https://s3.eu-west-1.amazonaws.com/production.users.storage/users_storage/users/eW_Dl8KdwpLCtsKlwqtywrHChcOXwrp0wqd4wp7DmsKXwpPCtMKpwqpywqnCrcKkwoZ4wqd4csKqwqBmwrU%3D/FRONT.png?AWSAccessKeyId=AKIAJEE33B4FZLU73WMA&Signature=FAJVUW99U4dkw4Ii85z%2BHpNd%2BsE%3D&Expires=1700739964",
"UTILITY_BILL": "https://s3.eu-west-1.amazonaws.com/production.users.storage/users_storage/users/eW_Dl8KdwpLCtsKlwqtywrHChcOXwrp0wqd4wp7DmsKXwpPCtMKpwqpywqnCrcKkwoZ4wqd4csKqwqBmwrU%3D/UTILITY_BILL.png?AWSAccessKeyId=AKIAJEE33B4FZLU73WMA&Signature=nZ0i5%2BJPzCDshWAgOj2HufZGtZs%3D&Expires=1700739964"
},
"additionalStepPdfUrls": {},
"AML": [
{
"status": {
"serviceSuspected": false,
"serviceUsed": true,
"serviceFound": true,
"checkSuccessful": true,
"overallStatus": "NOT_SUSPECTED"
},
"data": [
{
"name": null,
"surname": null,
"nationality": null,
"dob": null,
"suspicion": null,
"reason": null,
"listNumber": null,
"listName": null,
"score": null,
"lastUpdate": null,
"isPerson": null,
"isActive": null,
"checkDate": "2023-11-23 10:44:29"
}
],
"serviceName": "PilotApiAmlV2NameCheck",
"serviceGroupType": "AML",
"uid": "26b3ac22-89ed-11ee-ba61-0a201119565b",
"errorMessage": null
}
],
"LID": null,
"scanRef": "26b3ac22-89ed-11ee-ba61-0a201119565b",
"externalRef": null,
"clientId": "S53574N73T",
"startTime": 1700736224,
"finishTime": 1700736269,
"clientIp": "177.77.77.196",
"clientIpCountry": "LT",
"clientLocation": "Kaunas, Lithuania",
"manualAddress": null,
"manualAddressMatch": false,
"registryCenterCheck": null,
"addressVerification": null,
"questionnaireAnswers": null,
"companyId": null,
"beneficiaryId": null,
"riskAssessment": {
"risk_score": 38,
"risk_level": "MEDIUM"
},
"additionalSteps": {
"UTILITY_BILL": "COMPARE"
}
}
Webhook troubleshooting
Not receiving a callback? These are the first steps you should take in order to resolve an issue:
- Ensure that you have provided a valid callback endpoint (it does not contain typos and is a fully specified URL with HTTP schema, port and domain name).
- Ensure that the provided endpoint can be reached from internet.
- Ensure that your SSL is set up correctly. Our system can only send webhooks to URLs with valid SSL certificates.
- Ensure that you are actually not getting a callback and your framework is not accidentally returning some other HTTP response e.g. 422 or 500.