Dropbox stores an enormous variety of files, and often what you actually want from one is its content: the words spoken in a recording, the readable text of a document, or the technical metadata embedded in a file. Riviera, Dropbox's content-transformation platform, exposes public API endpoints for exactly this.
- /riviera/get_transcript_async turns audio and video into a transcript.
- /riviera/get_markdown_async turns documents (Office files, PDFs, Paper docs, HTML, and more) into Markdown.
- /riviera/get_metadata_async extracts structured metadata (EXIF, media, PDF, or Office) from a file.
In this guide, we'll cover:
Picking the right endpoint
Each endpoint answers a different question about a file, and you can use them independently:
- "What was said in this recording?": /riviera/get_transcript_async returns the spoken words as a time-aligned transcript.
- "What does this document say?": /riviera/get_markdown_async returns the document's body as Markdown.
- "What are this file's properties?": /riviera/get_metadata_async returns structured metadata for the file.
All three derive information from content already in Dropbox. They never modify the source file or write anything back to the user's Dropbox; the result is yours to store or process wherever your app lives. Each request processes a single file. To process multiple files, repeat the request for each file (see A typical end-to-end flow).
How the endpoints work: launch and poll
All three endpoints are asynchronous because the underlying processing can take time to complete. Transcription scales with the length of the recording, while document conversion and metadata extraction usually finish in seconds. Rather than block, you work in two steps:
1. Launch the job by calling the endpoint (for example, /riviera/get_transcript_async). You get back an async_job_id, an identifier representing your in-flight job.
2. Poll the endpoint's /check endpoint with that async_job_id. Each poll tells you the job is still in_progress, has finished (complete, with the result), or has failed (with a typed error).
A completed transcript poll, for instance, returns the structured result:
{
".tag": "complete",
"structured_transcript": {
"transcript_locale": "en",
"segments": [
{"text": "This is a test file for the AAC file format.", "start_time": 0.0, "end_time": 3.2},
{"text": "It contains a short spoken sentence.", "start_time": 3.2, "end_time": 5.8}
]
}
}
These routes are rate limited, so poll with back-off rather than in a tight loop. A poll interval on the order of 15 seconds per endpoint is a safe starting point, but treat it as guidance rather than a guarantee: honor any retry_after hint on a rate-limited (429) response and back off accordingly. Because the async_job_id fully represents the job, you don't have to launch and poll in the same process — a common pattern is to launch, persist the async_job_id, and let a separate worker check on it later. All three endpoints share this launch-then-/check shape, so one polling helper can serve them all.
Pointing at a file
Every endpoint takes the same input, file_id_or_url, and you supply exactly one of three ways to identify the file:
- A path, such as "/recordings/standup.mp4".
- A file ID, a stable ID you may already hold from another Dropbox API call. Prefer it over a path, since it survives renames and moves.
- A URL, either a Dropbox shared link or an external HTTP or HTTPS URL pointing at a supported file.
One behavior worth calling out: Dropbox shared links are resolved as the calling user and respect the link's own settings. A password-protected link, or one with downloads disabled, is rejected, because the API won't bypass a link's protections for you.
These endpoints support both user and app authentication:
- With User Auth, all three input types work: paths, file IDs, and URLs (including Dropbox shared links), each evaluated against that user's access.
- With App Auth (no user), only external URLs are supported. Paths, file IDs, and Dropbox shared links all require a user context and are rejected for app-only callers. This is why even a publicly accessible Dropbox shared link cannot be used anonymously — resolving a Dropbox link always requires a signed-in user.
For more information about retrieving and using Dropbox file paths and IDs, please refer to the File Access Guide.
Transcribing audio and video
Use /riviera/get_transcript_async when you have a recording and want its spoken content as text you can search, display, or summarize. Instead of one long string, the transcript comes back as an ordered list of segments, each carrying a span of text and the start/end offset where it occurs in the media. That timing is what lets you line words up against the timeline, whether for captions, jump-to-moment playback, or highlight-as-you-listen.
You control segment granularity with the timestamp_level parameter, which accepts sentence or word:
- sentence (the default) produces one segment per spoken sentence and suits most cases — readable transcripts, call summaries, and search indexing.
- word produces one segment per word, for alignment-sensitive features like precise caption timing or click-a-word-to-seek. It generates many more segments, so use it only when you need that precision.
The transcript also reports the language it detected, and you can pass a language hint (audio_language) when you already know it, which helps most on short or ambiguous clips. The reference documents these options and the exact response fields.
Converting documents to Markdown
Use /riviera/get_markdown_async when you want a document's content in a portable, uniform form — for indexing, diffing, feeding to an LLM, or rendering elsewhere. Riviera makes the format-specific decisions for you (how slides become sections, cells become tables, PDF layout becomes headings and paragraphs), so your code can treat every source format the same way from there on.
Two parameters change the output:
- enable_ocr recovers text baked inside an image — the classic case being a scanned PDF with no real text layer. It rescues content you'd otherwise lose, but it's noticeably slower, so enable it only when you expect scanned or image-based documents.
- embed_images inlines pictures directly into the Markdown as data URIs, producing a single self-contained artifact with no external references. The tradeoff is size, since embedded images can dwarf the text, so leave it off when your consumer only needs the words.
Choose the options based on your use case. For example, leave both options disabled for a search index, enable embed_images for a standalone viewer that renders images, and enable enable_ocr for scanned contracts. The reference lists the supported document types and the exact option fields.
Extracting file metadata
Use /riviera/get_metadata_async when you want a file's structured properties rather than its content. The kind of metadata returned depends on the file type: images return EXIF data (dimensions, camera, GPS, capture time), audio/video files return media metadata (duration, bitrate, per-stream codec info), PDFs return page/size metadata, and MS Office documents return document metadata (author, title, page/word/slide counts). The response's metadata_type field tells you which variant is populated. The reference lists the supported formats and the exact fields for each metadata type.
Handling failures
Failed jobs return structured, typed errors rather than plain-text messages, allowing you to handle different failure cases appropriately:
A typical end-to-end flow
Putting the pieces together, an integration usually looks like this:
- Locate the file. You have a path, a file ID from an earlier call, or a shared link from a user.
- Launch the (e.g., transcript, Markdown, or metadata) job and persist the async_job_id.
- Poll /check with back-off until the job is complete or failed.
- On success, take the result (e.g., transcript segments, Markdown string, or metadata) and store or process it in your own system.
- On failure, branch on the typed error per the table above: skip and log input problems, prompt the user about blocked links, retry transient failures.
To process many files, run this same flow per file: launch the jobs, track their IDs, and poll them in parallel. Because these endpoints have rate limits, build in back-off and be ready to retry a launch, not just a poll, and expect partial success — handling each file's outcome on its own. This flow is identical across all three endpoints, so it extends cleanly if Riviera adds more capabilities later.