Skip to Content
APICapture SDK

Capture SDK

@capturly/capture-sdk records on your user’s own device, from inside your own product. It is the browser half of the capture request flow: your backend creates the request and mints a per-person link, and the SDK records against it.

npm install @capturly/capture-sdk

Public package with no dependencies. The recording engine is bundled in.

Where the credentials live

Your secret key never reaches a browser. It grants your whole organization.

What the browser gets is the per-person capability from POST /v1/capture-requests/{id}/contacts — the contact id and the k query parameter of the returned URL. That capability is scoped to one person, revocable by removing the contact, and dead once the request’s deadline passes.

For a link anyone can use, take the request’s openLink.url and pass { mode: 'open', campaignId, key } instead.

Recording

import { CaptureClient } from '@capturly/capture-sdk'; if (!CaptureClient.isSupported()) { // No amount of retrying fixes this. Offer another browser. } const client = new CaptureClient({ access: { mode: 'contact', contactId, key }, }); const stream = await client.preview(); // triggers the permission prompt videoEl.srcObject = stream; await client.start(); // … const take = await client.stop(); // nothing has left the browser yet previewEl.src = take.previewUrl; // let them watch it back await client.submit({ releaseAccepted: true, onProgress: (fraction) => setBar(fraction), }); client.destroy();

preview() is separate from start() so people can see themselves before anything records. Between stop() and submit() the file sits in the browser’s own storage, so a take can be reviewed and re-recorded with discard() without anything reaching a server.

Call destroy() on unmount, or the camera light stays on after the person has navigated away.

If the request requires a release, read its release.text from the API and show it before recording, then pass releaseAccepted: true only if the person actually accepted it.

A consent record is written only when the request requires a release and you pass true. Passing true without showing them anything records an agreement that never happened, which is worse than none. Passing false on a request that requires a release refuses the submission rather than storing an unconsented take.

Errors

Every failure is a CaptureError with a code. What matters is whether retrying can work, and who has to act:

CodeMeaningRetry?
unsupported_browserNo MP4 recording in this browserNo — offer another browser
permission_deniedCamera or microphone refusedNo — your user must allow it
no_devicesNo camera or microphone foundNo — your user must connect one
not_recording, nothing_to_submitCalled out of orderNo — fix the call sequence
invalid_requestThe call or its payload was rejected — missing release, missing name, take too longNo — fix the input
link_expiredUnknown link, or a key that no longer matchesNo
request_closedThe capture request is closed, past its deadline, revoked, or the workspace has lapsedNo
supersededAnother take for this person landed firstNo — their other submission is the live one
quota_exceededNo room right now: take too large, or the request is at its capNo — the workspace owner must act
rate_limitedToo many requestsYes, after a pause
upload_failedStorage or the API failed server-sideYes
networkThe request never reached CapturlyYes

Only the last three are worth retrying as-is. Retrying anything above them re-uploads the whole take to fail the same way.

What it does not do

Multi-participant sessions. One person at a time. A room where several people record at once needs signaling, TURN and a join ceremony, and is not part of this package. Use the sessions API and the hosted recorder for that.

UI. You get a MediaStream and state events; you render the interface.

Transcription. Capturly delivers masters; transcription is your pipeline’s.

Learning that a take landed

The SDK is not told the recording id, and your backend should not trust the browser for it. Use the capture_request.submission_received webhook, or read GET /v1/capture-requests/{id}/submissions, then download through GET /v1/recordings/{id}.

Browser support

Chrome and Edge 124+, or Safari 16.4+, on a secure context. Firefox cannot record: it has neither MP4 MediaRecorder nor MediaStreamTrackProcessor, so CaptureClient.isSupported() is always false there. CaptureClient.isSupported() is the check to run before showing a record button.

Last updated on