Skip to content
Back to skills

Front Webhook

ASecurity

Handle Front webhook events for the support platform. Use when implementing conversation ingestion, message handlers, Front API integrations, or webhook signature verification.

  • 666 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added February 8, 2026
developmentjavascripttypescriptgojavabashnextjstestingapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill front-webhook --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Front Webhook?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Front Webhook
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-front-webhook/badge)](https://www.skillsdirectory.com/skills/majiayu000-front-webhook)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: front-webhook
description: Handle Front webhook events for the support platform. Use when implementing conversation ingestion, message handlers, Front API integrations, or webhook signature verification.
allowed-tools: Read, Grep, Glob, Edit, Write, Bash
---

# Front Webhook Handler

Front is the **source of truth for all conversations**. Every support interaction starts with a Front webhook.

## ⚠️ CRITICAL: Webhooks Send PREVIEWS, Not Full Data

**This is the #1 gotcha with Front integration.** Webhooks do NOT contain the full message body or sender email. You MUST fetch via API.

## Key Insight: Webhooks Send PREVIEWS

Front webhooks send **event previews**, NOT full data. You get:
- IDs and `_links` for resources
- Basic metadata

You do NOT get:
- Full message body
- Author email address
- Conversation history

**Must fetch full data via Front API** using the `_links` in the preview.

## Front Signature Verification

Front uses a **different format** than Stripe-style:

```typescript
// Headers from Front:
// x-front-signature: base64-encoded HMAC
// x-front-request-timestamp: milliseconds
// x-front-challenge: present during setup only

function verifyFrontSignature(timestamp: string, body: string, secret: string): string {
  // Format: HMAC-SHA256(timestamp:body), base64 encoded
  const baseString = Buffer.concat([
    Buffer.from(`${timestamp}:`, 'utf8'),
    Buffer.from(body, 'utf8'),
  ]).toString()
  return crypto.createHmac('sha256', secret).update(baseString).digest('base64')
}
```

## Challenge-Response for Setup

When creating/updating a webhook, Front sends a validation request:
- Header: `x-front-challenge: <random-string>`
- Must respond with: `{"challenge": "<value>"}`

```typescript
if (result.challenge) {
  return NextResponse.json({ challenge: result.challenge })
}
```

## Webhook Handler Pattern

```typescript
export async function POST(request: NextRequest) {
  const payload = await request.text()
  const secret = process.env.FRONT_WEBHOOK_SECRET

  // Build headers object
  const headers: Record<string, string> = {}
  request.headers.forEach((value, key) => { headers[key] = value })

  // Verify signature
  const result = verifyFrontWebhook(payload, headers, { secret })
  if (!result.valid) {
    return NextResponse.json({ error: result.error }, { status: 401 })
  }

  // Handle challenge during setup
  if (result.challenge) {
    return NextResponse.json({ challenge: result.challenge })
  }

  // Parse and dispatch to Inngest
  const event = JSON.parse(payload)
  if (event.type === 'inbound_received') {
    await inngest.send({
      name: SUPPORT_INBOUND_RECEIVED,
      data: {
        conversationId: event.payload.conversation.id,
        messageId: event.payload.target.data.id,
        _links: {
          message: event.payload.target.data._links?.self,
          conversation: event.payload.conversation._links?.self,
        },
      },
    })
  }

  return NextResponse.json({ received: true })
}
```

## Local Dev (ngrok)

For local testing, use a tunnel (ngrok) and point the Front webhook to the **exact** tunnel URL:

```bash
ngrok http 3000
# Use the https URL printed by ngrok as the webhook target
```

Do not use wildcard domains for webhook URLs.

## Front Event Types (Application Webhooks)

| Webhook Event | API Event | Description |
|--------------|-----------|-------------|
| `inbound_received` | `inbound` | Incoming message |
| `outbound_sent` | `outbound` | Outbound message sent |
| `conversation_archived` | `archive` | Conversation archived |
| `conversation_reopened` | `reopen` | Conversation reopened |
| `assignee_changed` | `assign`/`unassign` | Assignee changed |
| `tag_added` | `tag` | Tag added |
| `tag_removed` | `untag` | Tag removed |

## Fetching Full Data

```typescript
const front = createFrontClient(process.env.FRONT_API_TOKEN)

// Fetch full message
const message = await front.getMessage(messageId)
// message.body, message.author.email now available

// Fetch conversation history
const history = await front.getConversationMessages(conversationId)
```

## File Locations

- Webhook handler: `apps/front/app/api/webhooks/front/route.ts`
- Front API client: `packages/core/src/front/client.ts`
- Signature verification: `packages/core/src/webhooks/verify.ts`
- Event types reference: `docs/FRONT-EVENTS.md`

## Inbox ID Conversion

Front UI URLs use decimal IDs, but the API uses base-36 with a prefix.

**Formula:** `inb_` + base36(decimal_id)

**Example:**
- UI URL: `https://app.frontapp.com/settings/tim:571743/inboxes/edit/[PHONE]/settings`
- Decimal ID: `[PHONE]`
- Base-36: `3srbb`
- API ID: `inb_3srbb`

**JavaScript conversion:**
```typescript
function frontInboxId(decimalId: number): string {
  return `inb_${decimalId.toString(36)}`
}
// frontInboxId([PHONE]) → "inb_3srbb"
```

**List all inboxes via API:**
```bash
curl -H "Authorization: Bearer $FRONT_API_TOKEN" "https://api2.frontapp.com/inboxes" | jq '._results[] | {id, name}'
```

**Known Skill Recordings inboxes:**
| Product | Inbox ID | Email |
|---------|----------|-------|
| Total TypeScript | `inb_3srbb` | [EMAIL] |
| Pro Tailwind | `inb_3pqh3` | [EMAIL] |
| Epic Web | `inb_jqs2t` | [EMAIL] |
| Testing Accessibility | `inb_3bkef` | [EMAIL] |
| Just JavaScript | `inb_2odqf` | [EMAIL] |
| Pro NextJS | `inb_43olj` | [EMAIL] |
| AI Hero | `inb_4bj7r` | [EMAIL] |
| ScriptKit | `inb_41c3r` | [EMAIL] |
| Badass Courses | `inb_3mn7r` | [EMAIL] |

## Environment Variables

```bash
FRONT_WEBHOOK_SECRET=  # App signing key (32-char hex)
FRONT_API_TOKEN=       # API token for fetching data
```

## Reference Docs

- `docs/FRONT-EVENTS.md` - Event types and payload structure
- `docs/support-app-prd/76-front-integration.md` - Full integration spec

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.