Setting Up Webhooks
Developer guide to SheerID webhooks: set them up in MySheerID, receive the event payload, verify the signature, and act on each event by calling Verification Details.
Overview
SheerID sends an HTTP POST to a URL you configure whenever a verification reaches a notable event (for example, SUCCESS). The payload is intentionally minimal — it identifies the verification and the event, and never contains personally identifiable information (PII). To act on an event, use the verificationId from the payload to call the Verification Details endpoint — for example, to read the reward code when a verification succeeds.
Set up in MySheerID
Webhooks are configured per program in MySheerID:
- Open your program and find the Offer Delivery section.
- Select Account Tagging to reveal the Webhooks card.
- Click + Add on the Webhooks card to open the configuration dialog.
- Toggle webhooks On and enter your endpoint URL (the HTTPS URL SheerID will
POSTto). - Toggle on the specific events you want to receive.
- Click Save.
For the customer-facing walkthrough, see Setting up webhooks.
Payload
SheerID POSTs a JSON body identifying the verification and the event that fired:
{
"verificationId": "60fb1e229ca29b55dc92abf2",
"notifierEventType": "SUCCESS"
}
| Field | Type | Description |
|---|---|---|
verificationId |
string | The verification this event is for. Use it with Verification Details. |
notifierEventType |
string | The event that triggered the webhook. See Event types. |
notifierEventType is sent by Feature Webhooks. Older standard webhooks send only verificationId.
Event types
Select the events you want when configuring your webhook. The value below is what arrives in notifierEventType:
notifierEventType |
Fired when | Typical use |
|---|---|---|
SUCCESS |
The verification succeeded — the user is eligible. | Apply the discount and read the reward code via Verification Details. |
DOCUMENT_REJECTED |
An uploaded document was rejected during review. | Track document drop-off and review metrics. |
MAX_DOC_UPLOAD_ATTEMPTS |
The user exhausted the allowed document-upload attempts and is now ineligible. | Flag the user as ineligible in your system. |
REMINDER |
A reminder was sent to a user who has not completed verification. | Mirror the nudge in your own messaging. |
EMAIL_LOOP |
The user must confirm ownership of their email address (email-loop step). | Track email-ownership confirmation. |
SMS_LOOP |
The user must confirm ownership of their phone number (SMS-loop step). | Track phone-ownership confirmation. |
CONSOLATION |
A consolation (alternative) offer was extended to a user who did not pass. | Deliver the alternative offer. |
EXPIRATION |
The verification expired before completion (e.g. not finished within the program’s window). | Analyze abandoned/incomplete verifications. |
ERROR |
The verification ended in an error state. | Diagnose technical issues. |
ADD_SCHOOL_SUCCESS |
An Add School Request was approved. | Update your school list; see reading the outcome. |
ADD_SCHOOL_FAILURE |
An Add School Request was rejected. | Note the rejection; see reading the outcome. |
success arrives synchronously on the verification response and fires a SUCCESS event. The docUpload and pending steps are not themselves events — once document review resolves, the final outcome arrives as SUCCESS, DOCUMENT_REJECTED, MAX_DOC_UPLOAD_ATTEMPTS, or CONSOLATION. So a backend that fulfills on SUCCESS (and optionally handles the rejection events) covers the happy path without polling.
Act on an event
Because the payload is minimal, treat each event as a signal to fetch what you need from the API. Use the verificationId to call Verification Details:
GET /rest/v2/verification/<VERIFICATION_ID>/details HTTP/1.1
Host: services.sheerid.com
Authorization: Bearer <YOUR_ACCESS_TOKEN>
The reward code lives on the verification’s last response: read lastResponse.rewardData (a map of all reward codes and values) — and the deprecated lastResponse.rewardCode for single-code back-compat. See Offer Codes for the full reward model. Consumer details (personInfo) are returned only for tokens with the Customer PII role.
Reading an Add School Request outcome
ADD_SCHOOL_SUCCESS and ADD_SCHOOL_FAILURE carry only the verificationId. The outcome is stored in the verification’s metadata — read it from Verification Details, or from GET /verification/{verificationId}/metadata:
| Metadata key | Value |
|---|---|
addSchoolRequestDecision |
ADDED, ALREADY_EXISTS, or REJECTED. |
addSchoolRequestOrgId |
The organization ID (empty if rejected). |
addSchoolRequestOrgName / addSchoolRequestOrgType / addSchoolRequestOrgEmailDomains |
Organization details. |
addSchoolRequestCompletionTimestamp |
Completion time (Unix). |
Verify the signature
Every webhook POST includes an X-SheerID-Signature header: an HMAC-SHA256 signature of the raw request body. Verify it before trusting the request.
The shared secret (Secret Token) is viewable in MySheerID by an Account Owner with the API Access permission. It is scoped to your account and applies to all active programs.
The signature is the lowercase hex HMAC-SHA256 digest of the raw request body (never the re-serialized JSON — re-serializing can change bytes and break the comparison). A minimal Flask receiver:
import hashlib
import hmac
import os
from flask import Flask, request, abort
app = Flask(__name__)
# Your account's Secret Token, from MySheerID. Keep it out of source control.
SHARED_SECRET = os.environ["SHEERID_WEBHOOK_SECRET"]
@app.post("/sheerid-webhook")
def receive_webhook():
raw_body = request.get_data() # bytes — verify against these exact bytes
signature = request.headers.get("X-SheerID-Signature", "")
expected = hmac.new(
SHARED_SECRET.encode("utf-8"),
msg=raw_body,
digestmod=hashlib.sha256,
).hexdigest()
# Constant-time compare; reject anything that doesn't match.
if not hmac.compare_digest(expected, signature):
abort(401)
event = request.get_json()
# Process idempotently (deliveries may be retried) — see Retries below.
process_event(event)
return "", 200
Additional signing fields
Enabling extraSigningFields adds a timestamp and nonce to the request body for extra entropy (ask your account team to enable it). For a JSON webhook the body becomes:
{
"verificationId": "60fb1e229ca29b55dc92abf2",
"notifierEventType": "SUCCESS",
"timestamp": 1699999999999,
"nonce": "9f2c1e7b8a3d4f6012ab34cd56ef7890"
}
| Field | Description |
|---|---|
timestamp |
Unix UTC timestamp, millisecond resolution. |
nonce |
Single-use random token. |
The X-SheerID-Signature HMAC is computed over the exact raw body you receive, so always verify against the raw bytes (including these fields).
Connection timeouts
SheerID enforces timeouts on the request to your server. Exceeding either marks the delivery failed and triggers a retry.
| Timeout | Value | Applies to |
|---|---|---|
| Connection | 5000 ms | Establishing the TCP connection to your server. |
| Read | 10000 ms | Waiting for the response after the request is sent (and between response packets). |
Retries
Any response that is not HTTP 2xx is a failure. SheerID retries on this schedule:
| Failed attempt # | Retry after |
|---|---|
| 1 | immediately |
| 2 | 1 hour |
| 3 | 3 hours |
| 4 | 7 hours |
After the fourth retry the notification is considered failed and no further attempts are made. Make your handler idempotent, since a delivery may be retried.
Related
- Verification Details — fetch the full verification (including the reward code) after an event.
- Offer Codes — how reward codes are delivered and what to do when the pool runs out.
- Static IP Addressing — allowlist SheerID’s outbound webhook IPs.