@ear3/server
v0.1.8
Published
Server SDK for Ear3 — webhook verification (HMAC), session mint and retrieval.
Downloads
1,026
Readme
@ear3/server
🤖 AI agents / LLMs: start with
AGENTS.md, thenllms.txt— both bundled in this package (also at https://www.ear3.ai/llms.txt). They are the complete machine-readable guide: commands, ids, and the exact create → deploy → integrate flow. To make Claude Code pick this up automatically in your project, add one line to yourCLAUDE.md:@node_modules/@ear3/server/AGENTS.md
Server SDK for Ear3. Use this on your backend to create sessions
programmatically, fetch results, and verify inbound webhook signatures.
Runs on Node 18+ (native fetch + node:crypto).
For browser-side embedding, use @ear3/voice-interviewer instead.
Need an interviewId? The fastest, recommended way to create one is the
CLI — sign in once with npx @ear3/voice-config-cli login, then
npx @ear3/voice-config-cli create --name "…" --prompt "…" generates and deploys an
AI interview in a single command and prints the interviewId (npx @ear3/voice-config-cli
list shows existing ones). Or create it in the dashboard.
Install
npm install @ear3/serverQuick start
import { Ear3 } from '@ear3/server'
const ear3 = new Ear3(process.env.EAR3_CONFIG_CLI_KEY!) // sk_live_… or sk_test_…
// Create a session and email it to a respondent
const session = await ear3.sessions.create({
interviewId: 'dpl_a1b2c3d4',
participantName: 'Olena K.', // dashboard display name (optional)
participantExternalId: 'crm_42', // your own respondent id (optional)
metadata: { userId: '123', source: 'march-campaign' },
})
await sendEmail({
to: user.email,
subject: 'Quick interview',
body: `Tap here when you have 5 minutes: ${session.sessionUrl}`,
})Retrieve a completed session
const session = await ear3.sessions.retrieve('inv_…')
if (session.status === 'COMPLETED' && session.response) {
console.log('Transcript:', session.response.transcriptKey)
console.log('Summary:', session.response.summary)
}Verify webhooks
Ear3 posts events (interview.completed, interview.failed, …) to your
webhook endpoint. Always verify the signature before trusting the payload.
import express from 'express'
import { Ear3, SignatureVerificationError } from '@ear3/server'
const ear3 = new Ear3(process.env.EAR3_CONFIG_CLI_KEY!)
const app = express()
// IMPORTANT: read raw body — JSON parsing changes whitespace and breaks the
// signature.
app.post('/webhooks/ear3', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = ear3.webhooks.constructEvent(
req.body.toString('utf8'),
req.headers['ear3-signature'] as string,
process.env.EAR3_WEBHOOK_SECRET!,
)
if (event.type === 'interview.completed') {
// event.data is typed if you pass a generic: constructEvent<MyType>(…)
handleCompletion(event.data)
}
res.json({ received: true })
} catch (err) {
if (err instanceof SignatureVerificationError) {
return res.status(400).send('Invalid signature')
}
throw err
}
})Next.js App Router
// app/api/webhooks/ear3/route.ts
import { Ear3, SignatureVerificationError } from '@ear3/server'
const ear3 = new Ear3(process.env.EAR3_CONFIG_CLI_KEY!)
export async function POST(req: Request) {
const rawBody = await req.text() // raw, before any JSON.parse
const signature = req.headers.get('ear3-signature') ?? ''
try {
const event = ear3.webhooks.constructEvent(
rawBody,
signature,
process.env.EAR3_WEBHOOK_SECRET!,
)
if (event.type === 'interview.completed') {
await handleCompletion(event.data)
}
return Response.json({ received: true })
} catch (err) {
if (err instanceof SignatureVerificationError) {
return new Response('Invalid signature', { status: 400 })
}
throw err
}
}Constructor options
new Ear3(secretKey, {
baseUrl?: string // default https://app.ear3.ai
fetchOptions?: RequestInit // applied to every request
})Self-hosting / dev
const ear3 = new Ear3('sk_test_…', { baseUrl: 'http://localhost:3000' })Or set EAR3_BASE_URL once in your env and the SDK picks it up:
EAR3_BASE_URL=http://localhost:3000Resolution order: explicit baseUrl option > EAR3_BASE_URL env > default
(https://app.ear3.ai).
Errors
Ear3Error— HTTP failures from the API (.status,.code,.message)SignatureVerificationError— webhook signature mismatch, malformed header, expired timestamp (default tolerance 5 min)
Claude Code plugin
Using Claude Code? The Ear3 plugin's
/ear3:create-interview skill takes you from a plain-language topic to a
deployed interview (and the interviewId + key this package needs) in one
go — Claude also invokes it on its own when you ask to create an interview:
/plugin marketplace add https://www.ear3.ai/claude/marketplace.json
/plugin install ear3@ear3Not using Claude Code plugins? (Cursor, Codex, plain agents): copy
skills/create-interview/
into your project's .claude/skills/ — it works without the namespace,
as /create-interview.
License
MIT
