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

# API & Webhook Configuration

> Generate API keys and configure webhooks for Tenzo integration

<Info>
  **Available to all organizations** - Configure API access and webhooks for custom integrations, automated workflows, and real-time event notifications.
</Info>

## Overview

The Developer Tools section provides self-service configuration for API keys and webhooks, enabling you to:

* **API Keys**: Authenticate with Tenzo's Public API for pushing jobs and candidates
* **Webhooks**: Receive real-time interview results, meeting-note events, and other notifications from Tenzo

All organizations can access these features via **Settings → Developer Tools** in the Tenzo dashboard.

## Generating an API Key

API keys allow you to authenticate with Tenzo's Public API for pushing jobs and candidates programmatically. To get started:

<Steps>
  <Step title="Navigate to Developer Tools">
    Go to **Settings** → **Developer Tools** → **API Keys** in the Tenzo dashboard
  </Step>

  <Step title="Create a New API Key">
    Click **Generate New Key**
  </Step>

  <Step title="Name Your Key">
    Give your key a descriptive name to help you identify it later (e.g., "Production Integration" or "Development")

    Optionally, set an expiration date for added security
  </Step>

  <Step title="Copy and Save Your Key">
    <Warning>
      **Important:** Your API key will only be displayed once. Copy it immediately and store it in a secure location.

      You won't be able to view the full key again after closing this dialog.
    </Warning>

    Your key will start with `tenzo_pk_`
  </Step>
</Steps>

### Managing Your API Keys

Once created, you can view all your organization's API keys, including:

* Key name
* When it was created
* Last time it was used
* Total usage count

If a key is no longer needed or has been compromised, you can revoke it at any time. Revoked keys cannot be reactivated - you'll need to generate a new one.

<Note>
  Revocation may take up to 60 seconds to fully propagate. A recently used key can continue to authenticate during that window while cached verifications expire.
</Note>

<Tip>
  **Using the API:** Once you have your API key, see the [Tenzo Public API Documentation](https://api.tenzo.ai/v1/tenzo-api-documentation) for complete information on available endpoints and how to make API requests.
</Tip>

## Configuring Webhooks

Webhooks allow Tenzo to send real-time notifications to your system when events occur — for example, when a candidate completes an interview or a meeting note finishes post-processing. **All organizations** can configure webhooks for their custom integrations and automated workflows.

### Setting Up a Webhook

<Steps>
  <Step title="Navigate to Developer Tools">
    Go to **Settings** → **Developer Tools** → **Webhooks** in the Tenzo dashboard
  </Step>

  <Step title="Enter Your Webhook URL">
    Provide the URL where you want to receive webhook notifications from Tenzo

    * Your URL must use HTTPS
    * Your endpoint should accept POST requests with JSON payloads
    * Example: `https://api.yourcompany.com/webhooks/tenzo-interviews`
  </Step>

  <Step title="Set a Webhook Secret (Recommended)">
    Enter a webhook secret for signature verification. This allows you to verify that webhook requests are genuinely coming from Tenzo.

    <Warning>
      Save your webhook secret securely. You'll need it to verify webhook signatures.
    </Warning>
  </Step>

  <Step title="Activate Your Webhook">
    Toggle the **Enable webhook delivery** switch to start receiving webhook notifications
  </Step>

  <Step title="Save Your Configuration">
    Click **Save Configuration** to apply your settings
  </Step>
</Steps>

### Managing Your Webhook

You can update your webhook URL, change your webhook secret, or deactivate webhooks at any time from the Webhooks page in Developer Tools. You can also delete your webhook configuration entirely if no longer needed.

## How Webhooks Work

### Event Types

A single webhook URL receives every event type below. Switch on the top-level `event` field to dispatch:

* `interview.results` — fired when a candidate completes a phone screening
* `note.finalized` — fired when a meeting note completes post-processing (transcript, summary, and field extraction)

### `interview.results` Payload

When a candidate completes an interview, Tenzo will send a POST request with the following data:

```json theme={null}
{
  "application_remote_id": "app-123",
  "candidate_name": "John Doe",
  "candidate_phone": "+15551234567",
  "candidate_email": "john@example.com",
  "call_id": "uuid",
  "job_name": "Software Engineer",
  "call_duration": "05:30",
  "tenzo_interview_url": "https://app.tenzo.com/...",
  "question_completion_rate": 100,
  "overall_grade": 85,
  "call_summary": "Candidate performed well...",
  "question_results": [...],
  "call_transcript": "...",
  "sms_transcript": "...",
  "recording_url": "https://..."
}
```

### `note.finalized` Payload

Sent once after a meeting note finishes post-processing (transcript saved, summary generated, and field extraction complete). `meeting.type` is either `interview` or `intake`.

`extractedJobData` is populated for intake meetings; `extractedCandidateData` is populated for interview meetings; both keys are always present, and the one that doesn't apply is `null`. The payload carries only IDs for the linked candidate and job — use the [Tenzo Public API](https://api.tenzo.ai/v1/tenzo-api-documentation) to fetch names, emails, and other denormalized details so they don't go stale.

**Intake example** (`meeting.type: "intake"`):

```json theme={null}
{
  "event": "note.finalized",
  "orgId": "org-uuid",
  "noteId": "note-uuid",
  "timestamp": "2026-05-12T18:04:00Z",
  "meeting": {
    "type": "intake",
    "url": "https://meet.google.com/...",
    "startTime": "2026-05-12T17:30:00Z",
    "attendees": [
      { "email": "hm@yourco.com", "name": "Hiring Manager" },
      { "email": "recruiter@yourco.com", "name": "Recruiter" }
    ],
    "organizerEmail": "recruiter@yourco.com"
  },
  "candidateId": null,
  "jobId": "job-uuid",
  "remoteJobId": "GH-9001",
  "notes": {
    "sections": [
      {
        "key": "a1b2c3",
        "title": "Role overview",
        "questions": [
          {
            "key": "q1q2q3",
            "question": "What does the role involve day-to-day?",
            "answered": true,
            "response": "Leading API design for the platform team...",
            "suggested": false
          }
        ]
      }
    ]
  },
  "transcript": [
    {
      "type": "message",
      "start_ts": 0,
      "end_ts": 4200,
      "content": "Thanks for jumping on — let's start with the team.",
      "role": "Recruiter"
    },
    {
      "type": "message",
      "start_ts": 4200,
      "end_ts": 9100,
      "content": "Sure. We're a 6-person platform team, mostly senior...",
      "role": "Hiring Manager"
    }
  ],
  "summary": "Markdown meeting recap...",
  "extractedJobData": {
    "role_title": "Backend Engineer",
    "comp_band": "$180k-$220k",
    "job_description_html": "<p>About the role...</p>"
  },
  "extractedCandidateData": null
}
```

**Interview example** (`meeting.type: "interview"`) — same shape, but `extractedCandidateData` is populated and `extractedJobData` is `null`:

```json theme={null}
{
  "extractedJobData": null,
  "extractedCandidateData": {
    "years_experience": "8",
    "current_salary": "180000"
  }
}
```

Field notes:

* For intake meetings, the generated HTML job description is included under `extractedJobData.job_description_html`. Other keys in `extractedJobData` are the structured ATS fields configured for your org.
* `attendees` are derived from the transcript by unique email, in first-seen order. Name is best-effort.
* `candidateId` and `jobId` are `null` when the note is not linked to a candidate or job.
* `remoteJobId` is the ATS-side job ID (e.g. Greenhouse, Workday) for the linked job. `null` when there's no linked job, or when the job hasn't been pushed to an ATS yet.
* Top-level keys and the `meeting` block are camelCase. Inner objects on `transcript[]` and `notes` (e.g. `start_ts`, `end_ts`, `sections`, `questions`) are emitted in snake\_case as shown.
* The webhook payload omits Tenzo-internal coaching/QA annotations: the top-level `notes.annotations` array, the per-question `annotations` array, and the per-speaker `email` / message `id` on transcript entries are not sent. Attendee emails are surfaced on `meeting.attendees` instead.

### Signature Verification (Recommended)

If you provide a webhook secret, Tenzo will include an HMAC-SHA256 signature in the `X-Webhook-Signature` header. Verify this signature to ensure the request is from Tenzo:

```python theme={null}
import hmac
import hashlib

def verify_signature(payload, signature, secret):
    expected = hmac.new(
        secret.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
```

### Delivery & Retry Logic

* Tenzo will retry failed deliveries up to 3 times with exponential backoff (0s, 2s, 4s)
* Your endpoint should respond with a 200, 201, 202, or 204 status code to acknowledge receipt
* Requests timeout after 30 seconds per attempt
* Your endpoint should be idempotent to handle potential duplicate deliveries

<Note>
  For complete API reference and additional webhook details, see the [Tenzo Public API Documentation](https://api.tenzo.ai/v1/tenzo-api-documentation#tag/Results-Webhooks).
</Note>

## Troubleshooting

<AccordionGroup>
  <Accordion title="API Key Not Working">
    Make sure:

    * Your key hasn't expired or been revoked
    * You're using the complete key including the `tenzo_pk_` prefix
    * You're including it in the Authorization header as shown in the API documentation
  </Accordion>

  <Accordion title="Not Receiving Webhooks">
    Verify that:

    * Your webhook is marked as "Active"
    * Your endpoint URL is correct and publicly accessible
    * Your firewall isn't blocking Tenzo's requests
    * Your SSL certificate is valid
  </Accordion>
</AccordionGroup>

## Related Resources

* [Tenzo Public API Documentation](https://api.tenzo.ai/v1/tenzo-api-documentation) - Complete API reference and webhook details
* [ATS Integrations](/ats-integrations/overview) - Overview of ATS integration options

## Need Help?

Contact the Tenzo support team at [support@tenzo.ai](mailto:support@tenzo.ai) for assistance with API or webhook setup.
