Record a meeting and retrieve its outputs

This is the shortest supported path from a meeting in your product to generated documents in your storage. The OAuth client secret and service access token stay in your backend. Your browser receives only a short-lived upload instruction.

Examples use Rust because it is the first released server SDK. TypeScript and Python will be added as tabs on this page, not as separate guides.

Before you start

Ask your Revero organisation administrator for a client id and client secret with these scopes:

  • meetings:write to create meetings;
  • recordings:write to prepare and complete recordings; and
  • meetings:read to read meetings, operations and outputs.

Store both values as backend-only secrets. Choose the base URL for the Revero environment your organisation has been provisioned in; never guess one from a browser URL.

1. Create the backend client

The client exchanges your credentials through OAuth Client Credentials and caches the short-lived service token. Do not add a token route to your frontend.

1use revero::{ReveroClient, ReveroClientOptions, Scope};
2
3let revero = ReveroClient::new(ReveroClientOptions::new(
4 std::env::var("REVERO_BASE_URL")?,
5 std::env::var("REVERO_CLIENT_ID")?,
6 std::env::var("REVERO_CLIENT_SECRET")?,
7 vec![
8 Scope::MeetingsRead,
9 Scope::MeetingsWrite,
10 Scope::RecordingsWrite,
11 ],
12))?;

2. Create the meeting

external_id is your stable identifier. Reusing it returns the same meeting, which makes a lost response safe to replay. output_profiles names the documents you need. Put authoritative facts that generation must not reinterpret in locked_context.

1let meeting = revero
2 .create_meeting(&MeetingCreate {
3 external_id: "board-meeting-2026-09-04".into(),
4 output_profiles: Some(vec!["transcript".into(), "minutes".into()]),
5 locked_context: Some(serde_json::from_value(serde_json::json!({
6 "organisation": "Example municipality",
7 "meeting_kind": "Board meeting"
8 }))?),
9 language: Some("en".into()),
10 })
11 .await?;

External ids are unique inside your Revero organisation, not globally.

3. Prepare browser upload

Prepare one recording from your backend. Return only the upload object and the recording id to the browser. Preserve the method, URL and headers exactly; they are one short-lived instruction, not fields to reinterpret.

1let prepared = revero
2 .prepare_recording(
3 &meeting.id,
4 &RecordingCreate {
5 content_type: Some("audio/webm".into()),
6 },
7 )
8 .await?;
9
10// Send `prepared.id` and `prepared.upload` to your frontend.
11// Never send the Revero client secret or service access token.

The ready-made React component and headless alternative are shown in Record from a browser.

4. Upload directly, then complete from the backend

The browser sends the audio straight to the provided URL with the provided method and headers. Once that upload succeeds, call complete from your backend with an idempotency key that you persist with the command.

1let operation = revero
2 .complete_recording(&prepared.id, "complete:board-meeting-2026-09-04")
3 .await?;

If the network drops before you see the response, replay with the same key. Revero returns the same operation and does not start duplicate work. Use a new key only after a terminal failure explicitly says retryable: true.

5. Wait for authoritative operation state

Operations and meetings are authoritative; webhooks only reduce how long you wait before reading them. Every wait must have a deadline. The SDK honours Retry-After on 429, applies bounded backoff between other non-terminal reads, and returns the last typed operation when the deadline expires.

1let settled = revero
2 .wait_for_operation(
3 &operation.id,
4 WaitOptions::new(std::time::Duration::from_secs(10 * 60)),
5 )
6 .await?;
7
8if settled.status == PublicOperationStatus::Failed {
9 tracing::warn!(
10 code = ?settled.failure_code,
11 retryable = settled.retryable,
12 );
13}

Branch on stable error and failure codes, never on their human-readable messages.

6. Retrieve and store outputs

List the meeting outputs and fetch the completed ones. The returned content is your application data: copy it into the long-term storage and access-control model you promise your users. Revero’s temporary copy follows your organisation’s configured retention policy.

1for output in revero.list_outputs(&meeting.id).await? {
2 if output.status == PublicOutputStatus::Completed {
3 let document = revero.get_output(&output.id).await?;
4 customer_storage.put(&meeting.external_id, document).await?;
5 }
6}

7. Verify webhooks and reconcile

Verify Flyt-Signature against the raw request bytes before parsing JSON. Reject timestamps more than five minutes from your clock and accept any valid v1 signature during secret rotation. Deduplicate on the envelope id, also sent as Flyt-Event-Id; delivery is at-least-once and unordered.

1use revero::webhooks::{self, WebhookEnvelope};
2
3let event: WebhookEnvelope = webhooks::verify_and_parse(
4 signature_header,
5 signing_secret.as_bytes(),
6 raw_body,
7 unix_timestamp_now,
8 webhooks::DEFAULT_TOLERANCE_SECONDS,
9)?;
10
11if deduplication_store.insert_once(&event.id).await? {
12 // Follow the event links with the backend client. The API state wins if a
13 // webhook was delayed, duplicated or delivered out of order.
14 reconcile_from_revero(&revero, &event).await?;
15}

Return 2xx quickly. Revero retries 429, 5xx and timeouts, does not follow redirects, and permanently stops on other 4xx responses.

Give this task to a coding agent

Copy the prompt below into an agent working in your Rust/Axum and Next.js/React repository. It deliberately names the source of truth and the complete outcome.

Implement a production-shaped Revero integration in this repository.
Use the Revero documentation MCP server at
https://docs.revero.no/_mcp/server and the Rust examples at
https://docs.revero.no/welcome.md. Do not infer undocumented
URLs, fields, error codes or retry behaviour.
Backend (Rust with Axum): use the `revero` crate. Keep REVERO_CLIENT_ID,
REVERO_CLIENT_SECRET, the service access token and webhook signing secret on
the server. Create endpoints that create a meeting with external_id,
output_profiles and locked_context; prepare a recording; return only the
short-lived upload instruction and recording id to the browser; complete the
recording with a stable Idempotency-Key; wait for the operation with a hard
deadline while respecting Retry-After and rate limits; retrieve completed
outputs; and verify webhook signatures over the raw body before decoding the
typed envelope. Deduplicate webhooks by event id and reconcile every event by
reading authoritative API state.
Frontend (Next.js with React): use `@revero/recorder`. Provide both a page with
the ready-made ReveroRecorder component and a small headless example. Browser
code may call only the customer backend and the short-lived upload URL. It must
never accept or expose an OAuth client secret or Revero service access token.
Persist returned output documents in the customer application's own storage.
Add tests for replaying completion with the same idempotency key, a polling
deadline, Retry-After, duplicate webhook delivery, SSR import of the recorder,
and an interrupted local recording that resumes upload. Run formatting,
typechecking, tests and production builds, then report the commands and exact
results.

The Revero release harness runs this prompt against clean Axum and Next.js fixtures and compiles the result against the packed package candidates. A prose review is not considered verification.