> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.theary.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook System Overview

> Understanding how SNH AI webhook notifications work in the verification workflow

# Webhook System Overview

<p>
  <a href="/assets/downloads/guides/webhook-overview.pdf" download="webhook-overview.pdf">Download this guide</a>
</p>

Theary's webhook system provides real-time notifications throughout the verification lifecycle. This guide explains how webhooks fit into the overall verification workflow and when different webhook events are triggered.

## Verification Lifecycle

The verification workflow consists of several phases, each triggering specific webhook events:

```mermaid theme={null}
graph TD
    A[Order Created] --> B[Research Phase]
    B --> C[Contact Discovery]
    C --> D{Contacts Found?}
    D -->|Yes| E[Contact Plan Generated]
    D -->|No| F[Human Escalation]
    E --> G[Outbound Attempts]
    G --> H{Response Received?}
    H -->|Yes| I[Document Processing]
    H -->|No| J{Retry/Escalate}
    I --> K{Result Valid?}
    K -->|Yes| L[Verification Complete]
    K -->|No| M[Action Required]
    J --> F
    F --> N[Manual Intervention]

    E -.->|verification.notification| E1[Contact Plan Webhook]
    G -.->|verification.notification| G1[Outbound Attempt Webhook]
    H -.->|verification.notification| H1[Inbound Message Webhook]
    L -.->|verification.completed| L1[Completed Webhook]
    M -.->|verification.action_required| M1[Action Required Webhook]
    F -.->|verification.action_required| F1[Escalation Webhook]
```

## Webhook Event Types

SNH AI sends three types of webhook events:

### 1. verification.completed

**When it's sent:**

* Verification reaches a terminal outcome (VERIFIED, NO\_RECORD, WRONG\_ORG, THIRD\_PARTY)
* All verification data has been processed and validated
* Results are ready for consumption by your system

**What it contains:**

* Complete verification result with all extracted data
* Full employment or education verification object (provided vs verified fields)
* Discrepancy flag indicating if verified data differs from provided data
* Research decision log (audit trail of contact discovery)
* Education accreditation data (for EDUCATION searches)
* Terminal channel used (EMAIL, VOICE, or FAX)

**Use this to:**

* Update your applicant records with verified data
* Mark verifications as complete in your system
* Trigger downstream workflows (e.g., send results to hiring manager)
* Generate reports or analytics

### 2. verification.action\_required

**When it's sent:**

* Verification cannot proceed without manual intervention
* Third-party vendor is required (THIRD\_PARTY\_RECORD)
* External service failure (UPSTREAM\_ISSUE)
* Internal system error (SYSTEM\_FAILURE)
* SLA timeout exceeded (SLA\_REACHED)
* All automation exhausted (HUMAN\_ESCALATION)

**What it contains:**

* Reason code explaining why action is required
* Structured contact payload (employment, education, license, or reference)
* Metadata with troubleshooting context
* Research decision log (for HUMAN\_ESCALATION)
* Contact plan data (when escalating with research results)

**Use this to:**

* Alert your team or end-user about the issue
* Route to appropriate handler based on reason code
* Guide users to next steps (e.g., submit via third-party portal)
* Track verifications requiring intervention

### 3. verification.notification

**When it's sent:**

* Intermediate stages during the verification process
* Research completes and generates a contact plan
* Outbound attempt is made (email sent, call initiated, fax dispatched)
* Inbound response received (email reply, voicemail, fax received)

**What it contains:**

* Notification type (CONTACT\_PLAN, OUTBOUND\_ATTEMPT, INBOUND\_MESSAGE)
* Contact plan with discovered contacts and confidence scores
* Outbound attempt metadata (channel, destination, attempt number)
* Inbound message classification and summary

**Use this to:**

* Update UI in real-time with verification progress
* Show contact attempts to end users
* Track verification velocity and bottlenecks
* Log activity for compliance and audit purposes

## Webhook Configuration

Webhooks can be configured at two levels:

### Tenant-Level Configuration

Global webhook settings for your organization, stored in your tenant configuration. Contact your SNH AI account manager to configure tenant-level webhooks.

**Benefits:**

* Applies to all verifications automatically
* Centralized management
* No per-request configuration needed

### Request-Level Configuration

Per-verification webhook settings via the `webhookConfig` object in the order creation request.

**Benefits:**

* Override tenant defaults for specific orders
* Route different verification types to different endpoints
* Apply custom headers or authentication per order

**Example:**

Each value under `closeoutEndpoints` can be a **string URL** (legacy), one [WebhookTarget](/api-reference/schemas/webhook-target) object, or an **array** of URLs/objects. `fallbackEndpoint` accepts the same shapes.

```json theme={null}
{
  "webhookConfig": {
    "enabled": true,
    "secret": "your-webhook-secret",
    "retryAttempts": 3,
    "closeoutEndpoints": {
      "EMPLOYMENT": [
        {
          "url": "https://your-app.com/webhooks/employment",
          "events": ["verification.completed"]
        }
      ],
      "EDUCATION": [
        {
          "url": "https://your-app.com/webhooks/education",
          "events": ["verification.completed", "verification.notification"]
        }
      ]
    },
    "fallbackEndpoint": [
      {
        "url": "https://your-app.com/webhooks/all-events",
        "events": ["verification.action_required"],
        "searchTypes": ["EMPLOYMENT", "EDUCATION"]
      }
    ]
  }
}
```

## Webhook Routing

The API resolves webhook endpoints using the following priority:

1. **Search-specific endpoint**: If configured for the search type (e.g., EMPLOYMENT), that endpoint is used
2. **Fallback endpoint**: If no search-specific endpoint is configured, the fallback endpoint is used
3. **Event filtering**: Each target can specify which events it wants to receive (`events` array)
4. **Search type filtering**: Fallback targets can specify which search types they handle (`searchTypes` array)

When multiple targets match an event, the API delivers to all matching targets concurrently.

## Webhook Security

All webhooks are signed using HMAC-SHA256 for authenticity verification:

**Headers:**

* `X-Webhook-Signature`: `sha256=<hex>` format
* `X-Event-Id`: Unique delivery ID for idempotency
* `X-Event-Type`: Event type (verification.completed, etc.)
* `X-Search-Type`: Search type (EMPLOYMENT, EDUCATION, etc.)
* `X-External-Search-Id`: Your external identifier (if provided)

**Signature Verification:**

```javascript theme={null}
const crypto = require('crypto')

function verifyWebhookSignature(rawBody, signature, secret) {
  const signatureValue = signature.replace('sha256=', '')
  const expectedSignature = crypto.createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex')

  return crypto.timingSafeEqual(Buffer.from(signatureValue, 'hex'), Buffer.from(expectedSignature, 'hex'))
}
```

**Important:** Always verify using the raw request body bytes, not parsed JSON.

## Webhook Reliability

The system ensures reliable webhook delivery through:

**Retry Logic:**

* Configurable retry attempts (default: 3, max: 10)
* Exponential backoff with jitter (2s, 4s, 8s)
* Client errors (4xx) are not retried
* Server errors (5xx) are retried automatically

**Idempotency:**

* Each delivery has a unique `X-Event-Id`
* Use this header to detect and handle duplicate deliveries
* Webhooks may be retried, so ensure your handlers are idempotent

**Persistence:**

* All webhook deliveries are logged in the database
* Delivery attempts are tracked with status codes and errors
* Audit trail available for troubleshooting

**Timeouts:**

* Per-request timeout: 30 seconds
* Your endpoint must respond with 2xx within this time
* Heavy processing should be done asynchronously

## Research Decision Log

For verifications that complete research (contact discovery), webhooks include a `researchDecisionLog` object that provides full transparency into how contacts were found:

```json theme={null}
{
  "researchDecisionLog": {
    "timestamp": "2025-12-09T10:30:00Z",
    "organizationName": "Acme Corporation",
    "location": "San Francisco, CA",
    "decisions": [
      {
        "stage": "ORG_PREFERENCE_SEARCH",
        "decision": "PROCEED",
        "reasoning": "Found 2 contacts from organization preference table",
        "confidence": 0.95,
        "timestamp": "2025-12-09T10:30:01Z"
      },
      {
        "stage": "WEB_SEARCH",
        "decision": "SKIP",
        "reasoning": "Sufficient contacts from org preferences; web search not needed",
        "timestamp": "2025-12-09T10:30:02Z"
      }
    ]
  }
}
```

**Research Stages:**

* `ORG_PREFERENCE_SEARCH`: Organization-specific preferences and contacts
* `VERIFIED_CONTACTS_SEARCH`: Previously successful contacts
* `INTERNAL_SEARCH`: Internal Algolia knowledge base
* `PARENT_ORG_SEARCH`: Parent organization research
* `WEB_SEARCH`: OpenAI web research for contact discovery
* `CHANNEL_POLICY_CHECK`: Channel policy validation
* `FINAL_RESULT`: Final research outcome

## Education Accreditation

For EDUCATION searches, completed and action\_required webhooks include accreditation data:

```json theme={null}
{
  "accreditation": {
    "reference": {
      "name": "Stanford University",
      "date": "2022-06-12",
      "city": "Stanford",
      "state": "CA"
    },
    "matches": [
      {
        "campus": {
          "dapipId": "243744",
          "locationName": "Stanford University",
          "city": "Stanford",
          "state": "CA",
          "url": "https://www.stanford.edu"
        },
        "accreditation": {
          "statusLabel": "Accredited",
          "isAccredited": true,
          "accreditor": "WASC Senior College and University Commission",
          "validFrom": "1949-01-01",
          "validThrough": null,
          "reason": "Accredited from 1949-01-01 through present"
        }
      }
    ],
    "metadata": {
      "accreditationStatus": "accredited",
      "campusesConsidered": 1,
      "dataSource": "DAPIP Database",
      "checkedOn": "2025-12-02"
    }
  }
}
```

The system automatically:

* Looks up institutions in the DAPIP (Database of Accredited Postsecondary Institutions) database
* Checks accreditation status at the reference date (graduation or attendance)
* Provides campus details, accreditor information, and historical timeline
* Suggests alternate campuses when exact match is not found

## Best Practices

### 1. Acknowledge Quickly

Respond with HTTP 200 within 30 seconds and process asynchronously:

```javascript theme={null}
app.post('/webhooks', async (req, res) => {
  res.status(200).send('OK')
  processWebhook(req.body).catch(console.error)
})
```

### 2. Verify Signatures

Always verify HMAC signatures before processing:

```javascript theme={null}
const signature = req.headers['x-webhook-signature']
if (!verifyWebhookSignature(req.rawBody, signature, secret)) {
  return res.status(401).send('Invalid signature')
}
```

### 3. Handle Idempotency

Use `X-Event-Id` to detect duplicates:

```javascript theme={null}
const eventId = req.headers['x-event-id']
if (await isProcessed(eventId)) {
  return res.status(200).send('Already processed')
}
```

### 4. Handle All Event Types

Implement handlers for all three event types:

```javascript theme={null}
switch (event) {
  case 'verification.completed':
    handleCompleted(data)
    break
  case 'verification.action_required':
    handleActionRequired(data)
    break
  case 'verification.notification':
    handleNotification(data)
    break
}
```

### 5. Use HTTPS

Always use HTTPS endpoints. The API only sends to HTTPS URLs in production.

### 6. Monitor Delivery

Track webhook delivery status and set up alerts for failed deliveries.

## Next Steps

* [Webhook Integration Guide](/api-reference/guides/webhook-integration) - Implementation details and examples
* [Webhook Events Reference](/api-reference/webhooks/events) - Complete payload structures and examples
* [Error Handling](/guides/error-handling) - Handling webhook delivery failures
