### Integrating Incode Identity Verification into Lever ATS

This page covers integrating Incode identity verification into the Lever Applicant Tracking System (ATS). This integration uses Tines, a powerful automation platform, to orchestrate the verification flow between Lever and Incode. When a candidate reaches a designated hiring stage, Tines sends them a personalized verification link, captures the result, and writes it back to their Lever opportunity automatically.

This integration handles the full candidate verification lifecycle. It:

- Validates incoming Lever webhook signatures using HMAC-SHA256.
- Triggers only when a candidate reaches your designated verification stage.
- Fetches candidate details from Lever, generates an Incode verification link, and emails it to the candidate.
- Stores a trace record in Tines to map verification results back to the correct Lever opportunity.
- Listens for Incode verification status updates and writes results, including notes and tags, back to Lever automatically.

### Prerequisites  
Ensure you have the following before you begin:

| Requirement | Details |
| --- | --- |
| Tines | An active workspace with permission to create stories, credentials, and records. The Records feature must be enabled. |
| Lever ATS | An account with Admin or Super Admin access to create API keys and configure webhooks. |
| Incode | An active integration with integration ID, secret, and x-api-key available. |
| Email sending | Tines email delivery is used to send verification links. Ensure your workspace domain is allowlisted for outbound email. |

### Understand Flows
The automation runs as two independent flows that communicate through a shared Tines Records table.

#### Flow 1: Trigger and Send Verification
Fires when a candidate moves to the verification stage in Lever.

```undefined
[Stage change webhook] → [Validate signature] → [Check stage ID]  
        ↓  
[GET /candidates/{id}] → [Extract name & email] → [3 min delay]  
        ↓  
[GET Incode auth token] → [Generate verification link]  
        ↓  
[Capture Tines record] → [Send email to candidate]  
```

#### Flow 2: Receive Result and Update Lever
Fires when Incode posts a verification status event to your Tines webhook.

```undefined
[Status update webhook] → [Check event_type == succeeded]  
        ↓  
[Match trace ID → OpportunityID]  
        ↓  
[Add tag: biometric-verified] → [Post verification note]  
```

### Set Up the Lever Integration
#### Configure Credentials
This Tines story requires five credentials stored in your Tines workspace. Go to **Credentials** in the Tines sidebar and create each one as a Text credential.

| Credential name | Source | Description |
| --- | --- | --- |
| `lever_api_key` | Lever > Settings > Integrations > API Credentials | Your Lever API key. Used as a Bearer token to fetch candidate data, post notes, and add tags. |
| `incode_x_api_key` | Dashboard > API Keys | The `x-api-key` header value for Incode's authorization endpoint. |
| `incode_workforce_integrationid` | Dashboard > Integrations | The unique identifier for your Incode integration. |
| `incode_workforce_secret` | Dashboard > Integrations | The secret for your integration, used for server-side token generation. |
| `tines_api_key_lever_incode_records` | Tines > Your Profile > API Keys | A Tines API key used by the story to query its own Records table when matching a trace ID to an opportunity. |

### Set Up the Lever Webhook
Lever must send a webhook event whenever a candidate's pipeline stage changes.

1. **Copy the Tines webhook URL**: After importing the story, open the `lever_webhook` agent and copy the generated webhook URL:

```undefined
https://<your-tenant>.tines.com/webhook/<story-guid>/871aa415a5da64cdfa3fba63527c374b
```

2. **Create the webhook in Lever**: In Lever, go to **Settings** > **Integrations** > **Webhooks** and add a new webhook:

| Field | Value |
| --- | --- |
| URL | Your Tines webhook URL from above |
| Events | `candidateStageChange` |
| Signing token | Copy this value—you'll need it in the next step |

3. **Update the HMAC signature secret in the story**  
   Open the `validate_webhook_signature` agent in Tines and replace the placeholder signing token with your Lever webhook signing token:

```json
{
     "calculated_signature_raw": "<<HMAC_SHA256(LOCAL.concatened, \"YOUR_LEVER_SIGNING_TOKEN_HERE\")>>"
}
```

### Set Up the Incode Webhook
Incode must POST verification status events to a second Tines webhook endpoint. This drives Flow 2.

1. **Copy the verification status webhook URL**: After importing the story, open the `Get_verification_status_updates` agent and copy its webhook URL:

```undefined
https://<your-tenant>.tines.com/webhook/<story-guid>/59ab814228451619d85618e952e62c34
```

2. **Register the webhook in Incode**: In Dashboard, go to **Configuration > Webhooks** and configure a webhook pointing to the URL above. The story expects the following fields in the webhook payload:

```json
{
     "event_type": "verification.succeeded",
     "data": {
       "verification_trace_id": "<trace-id>",
       "failure_reason": "<status message>",
       "ip": "<candidate IP>",
       "user_name": "<candidate name>",
       "latitude": 0.0,
       "longitude": 0.0
     }
}
```

### Configure Your Lever Stage ID
The story triggers only when a candidate advances to a specific Lever pipeline stage. You must update the stage ID to match a stage in your own Lever environment.

1. **Find the stage ID in Lever**: Use the Lever API to list your pipeline stages:

```bash
curl -X GET https://api.lever.co/v1/stages \
     -H "Authorization: Bearer <your-lever-api-key>"
```

2. **Update the**`validate_stage` **agent**: Open the `validate_stage` agent in Tines and replace the stage ID in the trigger rule:

```json
{
     "type": "field==value",
     "value": "YOUR-STAGE-UUID-HERE",
     "path": "EXTRACT_DATA.TO_STAGE_ID"
}
```

### Import the Tines Story
1. **Download the story file**: Download `lever-incode-candidate-biometric-identity-verification.json` from your Incode account manager.
2. **Import into Tines**: In your Tines workspace, click **New Story > Import** and upload the JSON file. Tines then creates all agents, connections, and the Tines Records table automatically.
3. **Complete configuration**: Before enabling the story, confirm the following:
   - All five credentials are created in Tines.
   - Lever webhook is configured to POST to `lever_webhook`.
   - Lever HMAC signing token is updated in `validate_webhook_signature`.
   - Stage ID is updated in `validate_stage`.
   - Incode webhook is configured to POST to `Get_verification_status_updates`.
   - Email sender name and reply-to address is updated in the Send Email Action.
   - Redirect URL in `incode_verification` is updated to your domain.
4. **Enable and test**: Enable the story in Tines. Move a test candidate to your verification stage in Lever and confirm the story fires, the email is sent, and a record appears in the Tines Records table.

### Automation Agents Reference
The story contains 16 agents across both flows.

#### Flow 1: Send Verification
| Agent | Type | Description |
| --- | --- | --- |
| `lever_webhook` | WebhookAgent | Receives all `candidateStageChange` POST events from Lever. Responds with HTTP 200 immediately. |
| `validate_webhook_signature` | EventTransformationAgent | Computes HMAC-SHA256 of token and triggeredAt using your Lever signing secret and compares it to the received signature. |
| `check_signature` | TriggerAgent | Gate: passes only events where the computed signature matches the received signature. Rejects forged or replayed webhooks. |
| `extract_data` | EventTransformationAgent | Extracts `candidateId`, `opportunityId`, `fromStageId`, `toStageId`, and `stageName` from the webhook payload. |
| `validate_stage` | TriggerAgent | Gate: passes only events where `toStageId` equals your configured verification stage ID. |
| `get_candidate_details` | HTTPRequestAgent | Calls `GET /v1/candidates/{id}` on the Lever API to retrieve the candidate's profile. |
| `extract_candidate_details` | EventTransformationAgent | Parses name, email, and phone from the Lever response. Splits full name into `first_name` and `last_name` for Incode. |
| `Delay sending identity verification message` | EventTransformationAgent (delay) | Waits 3 minutes before continuing. Allows stage transitions to settle in Lever before sending the email. |
| `get_incode_token` | HTTPRequestAgent | Calls the Incode server-side authorization endpoint to obtain a short-lived session token using your integration credentials. |
| `incode_verification` | HTTPRequestAgent | Generates a personalized, time-limited verification link for the candidate. The link is valid for 72 hours. |
| `Capture Record` | RecordAgent | Creates a record in the `Lever_Incode_Information` table storing the opportunity ID, candidate ID, and Incode trace ID. |
| `Send Email Action` | EmailAgent | Sends an HTML-formatted email to the candidate with the verification link and instructions. |

#### Flow 2: Receive Result
| Agent | Type | Description |
| --- | --- | --- |
| `Get_verification_status_updates` | WebhookAgent | Receives POST callbacks from Incode containing verification results. Second entry point of the story. |
| `Trigger Action` | TriggerAgent | Gate: passes only events where `event_type == "verification.succeeded"`. |
| `Match_record_trace_opportunity` | HTTPRequestAgent | Queries the Tines Records API to find the opportunity ID matching the `verification_trace_id` from the Incode callback. |
| `update_lever_tag` | HTTPRequestAgent | Calls `POST /v1/opportunities/{id}/addTags` to add the `biometric-verified` tag to the candidate's Lever opportunity. |
| `update_lever_note` | HTTPRequestAgent | Posts a structured note to the Lever opportunity with the event type, status, trace ID, IP, name, and geolocation from the Incode verification result. |

### API Reference
#### Incode APIs
**Get Auth Token**

```undefined
POST https://demo-api-incode-id.incodesmile.com/v1/integration/authorize/server
x-api-key: CREDENTIAL.INCODE_X_API_KEY
```

Request body:

```json
{
  "integrationId": "CREDENTIAL.INCODE_WORKFORCE_INTEGRATIONID",
  "secret": "CREDENTIAL.INCODE_WORKFORCE_SECRET"
}
```

#### Lever APIs
**Get Candidate Details**

```undefined
GET https://api.lever.co/v1/candidates/{candidateId}
Authorization: Bearer CREDENTIAL.LEVER_API_KEY
```

Key response fields used:

```undefined
data.name        // full name — split into first/last
data.emails[0]   // primary email address
data.phones[0]   // primary phone number
```
