Skip to main content

The Fullview API

Programmatic, read-only access to your organisation's sessions, events, transcripts and AI summaries.

Written by Sofia

The Fullview API gives you programmatic, read-only access to your organisation's session data — the same sessions, transcripts and AI summaries you see in the Fullview dashboard. Use it to sync sessions into your data warehouse, enrich support tickets with what a user actually did, or build internal tooling on top of Fullview recordings.

Interactive reference: a full OpenAPI reference with a try-it-out console lives at https://api.<region>.fullview.io/public/v1/docs, and the raw OpenAPI document at …/public/v1/openapi.json.

Base URL

https://api.<region>.fullview.io/public/v1

Replace <region> with your organisation's region — eu1, eu2 or us1. It's the same region that appears in your Fullview dashboard URL.

All endpoints are versioned under /v1. Breaking changes will only ever ship under a new version prefix.

Authentication

Every request is authenticated with an organisation API key, sent as a bearer token:

Authorization: Bearer fv_Kj8mN2pQ7rT4vX9yA3bC6dF0gH5jL1nP8sV2wY4zR6k

Creating and managing keys

API keys are created in the Fullview dashboard under Settings → API keys (you need organisation-settings permissions, e.g. an admin role).

  • The key is shown once, at creation. Fullview stores only a hash — if you lose a key, revoke it and create a new one.

  • Keys start with the fv_ prefix, so secret scanners can recognise them. Treat them like passwords: keep them in a secret manager, never in client-side code or a repository.

  • Keys belong to your organisation, not to the user who created them. They keep working when that person leaves.

  • Keys don't expire — they live until you revoke them. Revocation takes effect immediately.

  • You can hold up to 10 active keys per organisation. Use separate keys per integration so you can revoke one without breaking the others.

  • The dashboard shows each key's masked form (fv_…R6k) and when it was last used.

Scoping

Every request is scoped to the organisation the key belongs to. There is no organisation or tenant parameter anywhere in the API — you can only ever see your own data.

Authentication errors

A missing, malformed, unknown or revoked key always returns the same 401 Unauthorized response, with no further detail.

Rate limits

  • 120 requests per minute per API key. The window is a fixed minute.

  • Every response carries X-RateLimit-Limit and X-RateLimit-Remaining headers, so a well-behaved client can slow down before being refused.

  • When you exceed the limit you get 429 Too Many Requests with a Retry-After header (in seconds). Wait that long and retry.

AI summary generation (see below) has its own, separate limits: 10 generations per minute and 300 per UTC day per organisation. Reading an existing summary never counts against these.

Errors

Errors are returned as RFC 9457 problem details — a real HTTP status code plus a small typed JSON body:

{
"type": "urn:fullview:api:error:not-found",
"title": "Not found",
"status": 404,
"detail": "No session with that id in your organisation."
}

The type values are stable strings you can match on:

Status

type

When

400

urn:fullview:api:error:invalid-request

A query parameter or path segment failed validation. detail says which.

401

urn:fullview:api:error:unauthorized

Missing, malformed, unknown or revoked API key.

403

urn:fullview:api:error:forbidden

The key lacks the scope this endpoint needs.

404

urn:fullview:api:error:not-found

No such resource in your organisation.

429

urn:fullview:api:error:rate-limit-exceeded

Rate limit or generation limit exceeded — see Retry-After.

500

urn:fullview:api:error:internal-error

Something failed on our side. 5xx bodies carry no detail.

Sessions

A session is one recorded stretch of a user's activity. It comes in two types:

  • meeting — a session with a call on it (a Fullview meeting or cobrowse).

  • replay — a browsing session recorded without a call.

List sessions

GET /v1/sessions

Returns your organisation's sessions, newest first by default, filtered server-side.

curl "https://api.eu1.fullview.io/public/v1/sessions?type=meeting&startedAfter=2026-08-01T00:00:00Z&hasTranscript=true&limit=100" \
-H "Authorization: Bearer fv_..."

Filters — all optional, combined with AND:

Parameter

Type

Description

startedAfter

ISO 8601

Only sessions that started at or after this time.

startedBefore

ISO 8601

Only sessions that started at or before this time.

type

replay | meeting

Session type.

customerId

string

Fullview customer identifier.

customerExternalId

string

The identifier your system knows the person by, as supplied to identify(). Exact match.

customerEmail

email

Exact match on the identified customer email.

agentEmail

email

Exact match on the email of the agent who joined the session.

environment

string

The environment reported at identify time, e.g. production.

minDurationSeconds

integer ≥ 0

Drop sessions shorter than this — useful for excluding bounces.

hasTranscript

boolean

Only sessions that do / do not have a finished transcript.

hasAudio

boolean

Only sessions that do / do not have recorded audio.

hasErrors

boolean

Only sessions with at least one console error.

hasRageClicks

boolean

Only sessions with at least one rage click.

order

asc | desc

Order by start time. Default desc (newest first).

limit

1–200

Page size. Default 50.

cursor

string

The nextCursor from the previous page.

Boolean parameters must be a literal true or false — anything else is a 400, not a guess.

Pagination is cursor-based:

{
"data": [ ... ],
"pagination": {
"nextCursor": "eyJ...",
"totalCount": 1284
}
}
  • Pass nextCursor back as cursor, together with the same filters — a cursor is only valid for the query that produced it.

  • nextCursor is null on the last page.

  • totalCount is the total matching the filters, regardless of cursor position.

  • Cursors stay valid indefinitely, so a paused export can resume later.

Get one session

GET /v1/sessions/{sessionId}

Returns the full session object:

{
"data": {
"id": "ses_9f2c1a7e3b90",
"type": "meeting",
"startedAt": "2026-08-19T09:58:12.000Z",
"endedAt": "2026-08-19T10:12:45.000Z",
"durationSeconds": 873,
"device": {
"browser": "Chrome 128",
"deviceId": "d_4c1a...",
"ipAddress": "203.0.113.7",
"environment": "production"
},
"participants": {
"customer": {
"id": "cus_71be...",
"name": "Ada Lovelace",
"email": "ada@example.com",
"externalId": "user-1815",
"isAnonymous": false
},
"agent": {
"id": "usr_20aa...",
"name": "Sam Agent",
"email": "sam@yourcompany.com"
}
},
"signals": {
"userSteps": 42,
"consoleErrors": 3,
"consoleWarnings": 7,
"rageClicks": 1
},
"recording": {
"callId": "call_88f0...",
"hasAudio": true,
"hasTranscript": true,
"transcriptStatus": "ready"
},
"hasSummary": true,
"isValid": true
}
}

Field notes:

  • participants.agent is null for a session nobody joined.

  • participants.customer.isAnonymous is true when the session was never associated with an identified person.

  • recording.transcriptStatus is one of ready, processing, failed, none — the same values the transcript endpoint reports.

  • hasSummary: false means "not generated yet", not "nothing to summarise" (summaries are generated lazily — see below).

  • isValid: false marks a session too short or incomplete to replay; these are excluded from list results.

Get a session's events

GET /v1/sessions/{sessionId}/events

Everything recorded during the session, grouped by what you'd ask of it. Each group is complete and chronological — no sampling, no pagination:

{
"data": {
"id": "ses_9f2c1a7e3b90",
"startedAt": "2026-08-19T09:58:12.000Z",
"userJourney": [
{ "timestamp": "...", "type": "dom.mouse.click", "target": "Submit expense", "url": "https://app.example.com/expenses" },
{ "timestamp": "...", "type": "dom.rage.click", "target": "Submit expense", "url": "https://app.example.com/expenses" }
],
"consoleEvents": [
{ "timestamp": "...", "level": "error", "message": "ValidationError: amount is required" }
],
"navigationEvents": [
{ "timestamp": "...", "url": "https://app.example.com/expenses" }
],
"networkRequests": [
{ "timestamp": "...", "method": "POST", "url": "https://api.example.com/expenses", "statusCode": 422, "durationMs": 310 }
]
}
}
  • userJourney is every user action in order: clicks, rage clicks, navigations and tab changes. An agent. prefix on the type marks an action taken by the agent during a call rather than by the user.

  • Navigations appear both in the journey (they're part of the user's story) and in navigationEvents (so "which pages" needs no filtering) — the groups are views over one event stream, not a partition of it.

Get a session's transcript

GET /v1/sessions/{sessionId}/transcript

The spoken transcript of the call on this session — chronological and speaker-attributed, one segment per consecutive same-speaker stretch:

{
"data": {
"id": "ses_9f2c1a7e3b90",
"status": "ready",
"source": "meeting",
"segments": [
{ "speaker": "Ada Lovelace", "text": "Can you see my screen now?", "offsetMs": 5230 },
{ "speaker": "Sam Agent", "text": "Yes — go ahead and open the expenses page.", "offsetMs": 8110 }
],
"actionPoints": [
{ "speaker": "Sam Agent", "text": "Send the updated expense policy to the customer after the call.", "offsetMs": 185000 }
]
}
}

An empty segments array is a normal answer, not an errorstatus says why:

status

Meaning

ready

Transcription finished; segments are complete.

processing

The call ended recently and transcription is still running — it finishes within minutes; poll again shortly. Segments may be partially filled.

failed

The pipeline reported an error; no transcript is coming.

none

This session can never have a transcript (no call, or the call was recorded without audio).

  • offsetMs is milliseconds from the session start, not epoch time. It matches the #[s:<seconds>] timestamps in the AI summary after dividing by 1000.

  • actionPoints are AI-extracted action items from the call, with the same offset basis.

  • For cheap discovery across many sessions, filter the list endpoint by hasTranscript instead of polling this endpoint per session.

  • 502 / 503 from this endpoint mean the transcript store is temporarily unavailable — safe to retry later.

Get a session's AI summary

GET /v1/sessions/{sessionId}/summary

An AI-written recap of what happened in the session — a few sentences plus an optional outcome — generated from the session's events and, for calls, the spoken transcript.

{
"data": {
"id": "ses_9f2c1a7e3b90",
"hasSummary": true,
"summary": "The user tried to submit an expense report and hit a validation error twice #[s:45]. An agent joined and completed the submission with them #[s:190].",
"outcome": {
"label": "resolved with agent assistance",
"severity": "neutral"
},
"generatedAt": "2026-08-19T10:15:00.000Z"
}
}

Summaries are generated lazily. hasSummary: false means "not generated yet", not "nothing to summarise". To generate one on the spot:

GET /v1/sessions/{sessionId}/summary?createIfMissing=true
  • The call is synchronous — expect a few seconds.

  • It is idempotent — the first result is cached permanently, and every later call returns it unchanged. generatedAt doubles as a cache validator: same generatedAt, same summary.

  • Generation is counted against per-organisation limits of 10 per minute and 300 per UTC day, separately from the request rate limit. A 429 tells you which via Retry-After — up to a minute for the per-minute limits, until the next UTC midnight for the daily one.

  • Reads of existing summaries never count against generation limits.

  • A 502 means generation failed and nothing was cached — retrying with createIfMissing=true is safe.

Field notes:

  • Inline #[s:<seconds>] tags in summary mark the moment each statement refers to, in whole seconds from the session start. Strip them with the pattern #\[s:\d+\] if you only want prose.

  • outcome.severity is neutral (ordinary or successful), warning (the user hit friction) or critical (they clearly failed at what they came to do).

  • outcome is null when no outcome could be confidently inferred.

Recipes

Nightly export of yesterday's meetings with transcripts:

curl "https://api.eu1.fullview.io/public/v1/sessions?type=meeting&hasTranscript=true&startedAfter=2026-08-25T00:00:00Z&startedBefore=2026-08-26T00:00:00Z&limit=200" \
-H "Authorization: Bearer fv_..."
 
# ...then page with `cursor` until nextCursor is null,
# and fetch /transcript per session id.

Find sessions where a specific user struggled:

curl "https://api.eu1.fullview.io/public/v1/sessions?customerExternalId=user-1815&hasRageClicks=true" \
-H "Authorization: Bearer fv_..."

Enrich a support ticket with a session recap:

curl "https://api.eu1.fullview.io/public/v1/sessions/ses_9f2c1a7e3b90/summary?createIfMissing=true" \
-H "Authorization: Bearer fv_..."

FAQ

Is there a write API?
No — v1 is read-only, plus on-demand summary generation. Session data is produced by the Fullview SDK and dashboard.

Can one key access multiple organisations?
No. A key belongs to exactly one organisation and can only read that organisation's data.

How fresh is the data?
Sessions become queryable shortly after they're recorded and indexed — typically within moments of the session ending. Transcripts finish within minutes of a call ending.

Can I be notified when something happens, instead of polling?
Yes — see the Webhooks article. Webhooks push meeting events to your endpoint as they happen; the session id in a webhook payload is the same id this API's GET /v1/sessions/{id} takes.

Did this answer your question?