Analysis rows with parent_session_id set, where N is the number of distinct conversations we detected. Could be 0, 1, or 100. Every existing analysis tool you’ve already integrated (get_analysis, webhooks, dashboards) Just Works on the children.
When to use it
| You have | You want | Use this |
|---|---|---|
| One recording, you know it’s a single call | One score, lowest cost | POST /v1/analyses |
| One recording, you don’t know if it’s one call or many | Auto-detect + score each | POST /v1/all-day-sessions ← you are here |
| One recording with multiple known calls inside | N scores, one per call | POST /v1/all-day-sessions |
| N separate URLs you already split | N scores | Loop POST /v1/analyses |
/v1/analyses (one call → 1 analysis result) with the boundary-detection layer added. Slightly more expensive because of the boundary detection step, but you save the Deepgram cost of any extra slicing pass.
Possible result counts
- 0 conversations — recording contained no sales conversation (pure internal/team meeting, music, silence). Session completes with
segments_detected: 0. No charges for V5 analysis since none ran. - 1 conversation — recording was a single continuous call. You get one analysis spanning roughly the full recording.
- N conversations — typical all-day case. One analysis per detected conversation, scored in parallel (max 5 concurrent).
The 60-second mental model
start_seconds/end_seconds offsets — your UI uses HTTP Range requests on your original URL to play any segment.
Submit a session
queued → transcribing → detecting → analyzing → completed.
Production keys only
Sandbox keys (pk_sandbox_…) cannot submit all-day sessions — they reject with invalid_recording_url. The reason: an all-day call burns real Deepgram + Gemini credits proportional to audio length, and sandbox is supposed to be free. Test with a single short recording via POST /v1/analyses using mock://* fixtures during development; switch to a pk_live_… key for real audio.
Wait for results
You have three patterns. Pick whichever matches your existing setup.Pattern 1 — webhooks (recommended)
Register a webhook for the events you care about. Most partners already haveanalysis.completed wired in for standalone analyses — that handler runs unchanged for each segment, since segments are first-class Analyses with parent_session_id set.
| Event | When it fires | Use it for |
|---|---|---|
all_day_session.processing_started | Worker picks the session up off the queue | ”Parlay has it” reassurance on backed-up days — fires before transcription starts so even an 8-hour file doesn’t sit in queued silence |
all_day_session.detected | Boundaries found, before per-segment analysis starts | Show the partner “we found 5 calls” while analyses are still processing |
analysis.completed | Each segment finishes (existing event, fires N times) | Existing per-call coaching workflows |
all_day_session.completed | All segments terminal | Rollup view (“Alex’s Tuesday at a glance”) |
all_day_session.failed | Unrecoverable error (no speech, transcription failed, etc.) | Surface a partner-friendly error + offer retry |
Pattern 2 — poll the session
status === "completed". Typical end-to-end latency is 1.5 minutes for a 24-minute recording, 5–10 minutes for a full 8-hour day with 5 segments.
Pattern 3 — wait inline (the MCP tool does this for you)
If you’re calling from an AI agent, thesubmit_all_day_recording MCP tool waits up to 10 minutes for completion and returns the full session inline.
What you get back
Oncestatus === "completed", the session response carries every detected segment plus their analysis_id forward-links:
GET /v1/analyses/:analysis_id to get the full canonical 6-pillar analysis (clarity, influence, objection, discovery, delivery, close + feedback_v5 + action_plan_v5). The shape is identical to a standalone analysis — same fields, same KPIs, same nesting. The only difference is parent_session_id is set.
Playing back a segment in your UI
This is the part that surprises new partners. You don’t ask Parlay for the segment audio — we don’t host it. You already have the original recording on your bucket. The session response gives youstart_seconds and end_seconds, plus a ready-to-paste playback block on every segment that bundles both the browser snippet and an ffmpeg one-liner:
browser_seek_js snippet into any page that has an <audio> element pointing at the original URL. Modern browsers issue HTTP Range requests automatically — only the relevant byte range downloads. Same behavior Spotify and Twitch use. No new file, no Parlay-hosted URL, no second download.
Offline export — the ffmpeg_extract command uses -c copy (stream-copy, no re-encoding) so it runs instantly and preserves the original quality. Pipe it into a script if you want to archive every conversation as a separate file:
recording_url echoed back in the playback block is the same URL you submitted, so you can swap it for a freshly-signed one if needed.
Backend requirements
The two playback patterns above (browser HTTP Range, server-side ffmpeg) have very different runtime needs. Worth knowing before you wire this into your stack.HTTP Range playback (browser)
Works in any frontend with no special backend. Modern browsers issue Range requests automatically against an<audio> or <video> element. The byte fetching happens between the browser and your bucket. Parlay is not in the path. No timeout limits, no ffmpeg binary needed, no server-side compute.
This is the easiest pattern and covers most use cases.
Server-side ffmpeg extraction
Theffmpeg_extract recipe in the playback block is a shell command. Running it requires:
- The
ffmpegbinary on the machine executing the command - Enough wall-clock time for the slice to copy (usually under 5 seconds for
-c copystream-copy, but depends on file size and source bucket latency) - Filesystem write access
- Outbound network access to the audio URL
| Runtime | ffmpeg available? | Typical wall-clock cap | Notes |
|---|---|---|---|
| Cloud Run / Render / Fly / Railway | No by default, easy to add via Dockerfile | Minutes to hours | Long-running OK |
| AWS Lambda | No by default, needs a layer or custom container image | 15 min hard cap | Custom layer is well-documented |
| Vercel Functions (Node) | No, no easy way to add | 10s Hobby, 60s Pro, 300s Enterprise | Not a good fit for ffmpeg |
| Vercel Edge Functions | No | 25s streaming, 30s total | Not a good fit |
| Cloudflare Workers | No, native binaries unsupported | 30s CPU | Not a good fit |
| Supabase Edge Functions (Deno) | No, WASM-only environment | 150s | Possible via ffmpeg.wasm, slow |
| Next.js API routes (Vercel) | Same as Vercel Functions | Same as above | Same caveats |
- Skip extraction entirely. Use browser HTTP Range for playback. Many partners don’t need server-side slicing at all.
- Run the ffmpeg work elsewhere. Push a job to a queue (SQS, Cloud Tasks, Cloudflare Queues, Supabase pg_cron) and pick it up from a long-running worker.
- Trigger ffmpeg from a container service. Cloud Run and Render both have generous free tiers and run unmodified Dockerfiles with ffmpeg pre-installed.
Webhook handlers specifically
Theall_day_session.* and analysis.completed webhook events arrive at whatever URL you registered. Your handler should return a 2xx response in a few seconds. If your handler is on a runtime with a short execution window (Vercel default 10s, Cloudflare Workers 30s CPU), running ffmpeg synchronously inside the handler will either time out or drop the response. The pattern that works in every runtime: acknowledge the webhook immediately, push the heavy work onto a queue or worker. The webhooks guide has more on this.
What we host vs what you host
To be explicit about the boundary:| Thing | Hosted by |
|---|---|
| The audio file | You |
| Transcription, boundary detection, V5 analysis | Parlay |
| Slice extraction (ffmpeg, byte range) | You |
| The analysis records and segment metadata | Parlay |
| Webhook delivery + signing | Parlay |
| Webhook receiver | You |
playback block is a convenience set of recipes that operate on your URL. How and where you run them is up to you.
Confidence + auto-analyze threshold
Each detected segment carries a confidence score from 0.0 to 1.0. The default behavior:| Confidence | Default action | Status |
|---|---|---|
| ≥ 0.8 | Auto-analyze with V5 | analyzed |
| 0.5 – 0.8 | Skip auto-analysis, surface for human review | needs_review |
< 0.5 | Skip entirely (probably not a sales conversation) | skipped |
auto_analyze: false returns the detected segments without running V5 — useful when you want to filter or review before paying for analysis.
Resolving playbooks per segment
Per-segment analyses run through the standard playbook resolver, which means per-rep playbooks just work. If you’ve created a rep-level playbook foralex-smith (via POST /v1/orgs/:org_id/playbooks with rep_id set), every segment in this session uses Alex’s playbook automatically. No extra config needed at the session level.
The full resolution order at analysis time:
options.playbook_content(request literal)options.playbook_id(explicit id)- Active rep playbook for
rep_id - Active org playbook for
org_id - None (default scoring)
Pricing model
Two billable events per session, both itemized on your usage report:| Event | Trigger | Approximate cost |
|---|---|---|
all_day_transcription | One Deepgram call on the full input | ~$0.30 per hour of audio |
all_day_detection | Single-shot Gemini boundary call | ~$0.05 per session |
analysis (existing) | One per analyzed segment | ~$0.06 per segment |
- Deepgram: ~$2.40
- Boundary detection: ~$0.05
- 5 × analysis: ~$0.30
- Total: ~$2.75
/v1/analyses — same five analyses, but you’d pay Deepgram five times instead of once. For an 8-hour day, all-day sessions are roughly 4× cheaper than the equivalent N standalone analyses.
Limits + edge cases
| Limit | Value |
|---|---|
| Audio length supported | 1 minute to 12 hours. Anything beyond 12 hours is best-effort and may hit Gemini boundary-detection ceilings around 15K utterances. |
| Min length | No hard minimum, but a recording shorter than ~30 seconds usually has no usable speech and will fail at the Deepgram step. |
| Max segments per session | No hard cap. 0, 1, or N are all valid. Typical days produce 5–15. |
| Concurrent V5 analyses per session | 5 |
| Audio formats | MP3, WAV, M4A, AAC, OGG, FLAC, OPUS, WEBM, MP4 |
| URL must be | HTTPS, no auth headers required, byte-range-fetchable |
Submitting via MCP
For AI-agent workflows, the@goparlay/mcp-server package exposes four conversational tools:
“My rep recorded their whole Tuesday — here’s the URL. Tell me what happened and which calls need follow-up.”
The AI calls submit_all_day_recording, gets the segments, summarizes each (Acme won, Beta Corp ongoing, Charlie skipped low-signal, Delta pricing pushback), and surfaces just the high-signal ones for review. One prompt, one URL, the rep’s whole day analyzed.
What we deliberately do NOT do
- No second transcription. We Deepgram the full file ONCE; per-segment analyses run on the sliced text. You don’t pay Deepgram per segment.
- No re-hosted audio. No
segment_audio_urlfield exists. Use HTTP Range requests on your original URL to play. - No human-review UI. Segments below the confidence threshold are surfaced via
needs_reviewstatus — what your dashboard does with them is up to you. - No automatic merge across multi-day recordings. One session per recording; if a rep records two 4-hour days separately, that’s two sessions.
See also
POST /v1/all-day-sessionsAPI reference- Webhooks guide — handling
all_day_session.*events - Playbooks guide — per-rep playbooks for personalized scoring
@goparlay/mcp-server— AI-native client install

