Developers
Build on Neural Summary
Send calls and meetings in over a REST API. Get the summary, action items, and any Lens you ask for back as signed webhooks. No new tool for your team, no copy-paste.
How it works
You push a recording with optional metadata. Neural Summary transcribes it, writes the summary, and generates the Lens outputs you requested. When it is done, we send a signed webhook to your endpoint. From there you route the result anywhere: a CRM, an inbox, a database, or an automation in Zapier, Make, or n8n.
One recording in, structured outputs out.
Quick start
1. Create an API key. Open Settings › Integrations and copy the key. It is shown once.
# Create an API key in Settings > Integrations. It is shown once.
API_KEY="ns_live_..."2. Upload an audio file. Multipart is fine for small files.
curl -X POST https://neuralsummary.com/integrations/v1/transcriptions \
-H "Authorization: Bearer $API_KEY" \
-F "audio=@./call.mp3;type=audio/mpeg" \
-F 'metadata={"title":"Call with Acme","callMetadata":{"callerName":"Jane Doe","callerEmail":"jane@acme.com"},"integrationOptions":{"lensTemplates":["followUpEmail"]}}'3. Receive the result. Subscribe a webhook (recommended) or poll the transcription until it is complete.
curl -H "Authorization: Bearer $API_KEY" \
https://neuralsummary.com/integrations/v1/transcriptions/<id>Authentication
Every /integrations/v1/* request needs an API key, in either header:
Authorization: Bearer ns_live_...x-api-key: ns_live_...
Keys are managed at Settings › Integrations. We store a bcrypt hash plus the first characters for display, never the raw key. Revoke any key from the same screen and it stops working immediately. A key inherits its owner’s plan limits, and integration uploads count against the same monthly quota as the app.
Endpoints
/integrations/v1/transcriptionsMultipart upload, suitable for files up to about 100 MB. Send the file as audio and an optional JSON metadata field with title, callMetadata, and integrationOptions. Returns a transcription with status pending.
/integrations/v1/upload-urlFor large recordings. Ask for a signed storage URL, upload the file directly to it, then call from-storage. The audio never passes through our API.
{
"fileName": "long-call.m4a",
"contentType": "audio/x-m4a",
"fileSize": 524288000
}You receive an uploadUrl and a storagePath. PUT the bytes with the exact same Content-Type. The URL expires in one hour.
/integrations/v1/transcriptions/from-storageAfter the PUT completes, tell us the file is ready. We only accept a storagePath we issued.
{
"storagePath": "uploads/<your-uid>/...long-call.m4a",
"fileName": "long-call.m4a",
"fileSize": 524288000,
"contentType": "audio/x-m4a",
"title": "Sales call with Acme",
"callMetadata": { "callerEmail": "jane@acme.com" },
"integrationOptions": { "lensTemplates": ["followUpEmail", "salesEmail"] }
}/integrations/v1/transcriptions/:idPolling fallback for callers that cannot receive webhooks. Returns the full transcription, populated with results once status is completed. Poll no faster than once every 10 seconds. Typical latency is 30 to 60 seconds per recording minute.
Lens templates
Pass any of these IDs to integrationOptions.lensTemplates to have them generated alongside the summary. The summary is always produced. Unknown IDs return 400 Bad Request.
| ID | Output |
|---|---|
followUpEmail | Ready-to-send follow-up email |
salesEmail | Sales follow-up email with proof points |
actionItems | Owners-and-dates action list |
meetingMinutes | Formal meeting minutes |
oneOnOneNotes | 1:1 notes |
agileBacklog | User stories with acceptance criteria |
prd | Product requirements doc |
retrospective | Retro themes and improvements |
crmNotes | CRM-shaped notes (BANT, stakeholders) |
dealQualification | MEDDICC qualification |
objectionHandler | Objections and suggested responses |
blogPost | Drafted blog post |
linkedinPost | LinkedIn post variants |
Webhooks
Add an endpoint at Settings › Integrations › Webhooks. We send a signed POST when an integration-sourced transcription finishes. Subscribe to either or both events: v1.transcription.completed and v1.transcription.failed.
Delivery is retried on any non-2xx response.
Payload
{
"id": "evt_aBcDeFgHiJkLmNoP",
"event": "v1.transcription.completed",
"createdAt": "2026-05-16T10:13:45.123Z",
"data": {
"transcriptionId": "tr_xyz",
"title": "Call with Acme",
"durationSeconds": 312,
"detectedLanguage": "english",
"callMetadata": {
"callerName": "Jane Doe",
"callerEmail": "jane@acme.com",
"callerPhone": "+31612345678",
"callSource": "ringcentral",
"externalRef": "call_98765"
},
"summary": { "...": "SummaryV2: title, intro, sections, decisions, nextSteps" },
"generatedAnalysisIds": ["ana_abc", "ana_def"],
"conversationCategory": "sales-call",
"completedAt": "2026-05-16T10:13:45.000Z"
}
}Verify the signature
The X-NeuralSummary-Signature header is t=<unix-seconds>,v1=<hex>. The HMAC is SHA-256 of <seconds>.<raw-body> with your webhook secret. This mirrors Stripe’s format. Reject anything older than five minutes.
const crypto = require('crypto');
// Header format: t=<unix-seconds>,v1=<hex-hmac>
function verify(rawBody, headerValue, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(
headerValue.split(',').map((p) => p.split('=')),
);
const ts = parseInt(parts.t, 10);
if (Math.abs(Date.now() / 1000 - ts) > toleranceSeconds) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${ts}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(parts.v1, 'hex'),
);
}Retries
A non-2xx response is retried up to five times with exponential backoff: 30s, 1m, 2m, 4m, 8m. We do not retry 4xx responses other than 408 and 429. Failed deliveries appear under Recent deliveries with a one-click Replay.
Zapier, Make, and n8n
You do not need to write a webhook handler. Zapier, Make, and n8n all have a generic HTTP action that calls our endpoints, plus a catch-hook trigger that receives our webhook. A typical recipe turns a recorded call into a drafted follow-up email:
Call recording to follow-up draft, with no code.
The shape is the same in each tool: use the HTTP module/action for the three upload calls, and the catch-hook trigger for the webhook. Take data.callMetadata.callerEmail from the webhook as the recipient, and fetch the Lens body with the polling endpoint using data.transcriptionId.
Limits and security
- Enterprise plan required (admins bypass for testing).
- Rate limits per key: 30 multipart uploads, 60 upload-URL mints, and 120 polling reads per minute.
- Up to 5 GB per file, matching the Enterprise upload limit.
- Webhook URLs must use HTTPS in production. Loopback hosts are rejected.
- API keys are bcrypt-hashed at rest. Webhook secrets are shown on demand; treat them like a password.
Start building
Generate an API key in Settings › Integrations and send your first recording. Need a hand? hello@neuralsummary.com.