> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.dropboxapi.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.dropboxapi.com/_mcp/server.

# Extracting Content and Metadata with Riviera

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](/riviera/api-reference/riviera/get-transcript-async) turns audio and video into a transcript.
* [/riviera/get\_markdown\_async](/riviera/api-reference/riviera/get-markdown-async) turns documents (Office files, PDFs, Paper docs, HTML, and more) into Markdown.
* [/riviera/get\_metadata\_async](/riviera/api-reference/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](#picking-the-right-endpoint)
* [How the endpoints work: launch and poll](#how-the-endpoints-work-launch-and-poll)
* [Pointing at a file](#pointing-at-a-file)
* [Transcribing audio and video](#transcribing-audio-and-video)
* [Converting documents to Markdown](#converting-documents-to-markdown)
* [Extracting file metadata](#extracting-file-metadata)
* [Handling failures](#handling-failures)
* [A typical end-to-end flow](#a-typical-end-to-end-flow)

## 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](/riviera/api-reference/riviera/get-transcript-async) returns the spoken words as a time-aligned transcript.
* **"What does this document say?"**: [/riviera/get\_markdown\_async](/riviera/api-reference/riviera/get-markdown-async) returns the document's body as Markdown.
* **"What are this file's properties?"**: [/riviera/get\_metadata\_async](/riviera/api-reference/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](#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](/riviera/api-reference/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:

```json
{
  ".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](/dropbox-api/docs/auth-types#user-authentication), all three input types work: paths, file IDs, and URLs (including Dropbox shared links), each evaluated against that user's access.
* With [App Auth](/dropbox-api/docs/auth-types#app-authentication) (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](/dropbox-api/docs/file-access).

## Transcribing audio and video

Use [/riviera/get\_transcript\_async](/riviera/api-reference/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](/riviera/api-reference/riviera/get-transcript-async) documents these options and the exact response fields.

## Converting documents to Markdown

Use [/riviera/get\_markdown\_async](/riviera/api-reference/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](/riviera/api-reference/riviera/get-markdown-async) lists the supported document types and the exact option fields.

## Extracting file metadata

Use [/riviera/get\_metadata\_async](/riviera/api-reference/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](/riviera/api-reference/riviera/get-metadata-async) 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:

| Failure group                          | Example causes                                                              | What to do                                                                                                                                                                                 |
| -------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **File not usable for this operation** | No audio track for transcription; an unsupported document type for Markdown | Confirm the file is a supported type for this endpoint and actually contains the expected content (e.g. a real audio track). Retrying the same file won't help — fix the input or skip it. |
| **File can't be reached**              | Doesn't exist, not accessible to the caller, or is a folder                 | Verify the identifier points at a file (not a folder) and that the authenticated caller has access to it, then retry with a corrected identifier.                                          |
| **Shared link blocked the request**    | Password-protected link, or downloads disabled                              | The link's own settings block access. Ask the user to supply a link without a password/with downloads enabled, or point at the file by path or file ID instead.                            |
| **A limit was exceeded**               | Media too long or a document too large                                      | Check the file against the published size/duration limits. Retrying won't help — skip it or split/shrink the input to fit.                                                                 |
| **Server-side problem**                | Transient or unexpected backend failure                                     | Retry the request with exponential back-off; if it keeps failing, surface it for investigation.                                                                                            |

## A typical end-to-end flow

Putting the pieces together, an integration usually looks like this:

1. **Locate the file**. You have a path, a file ID from an earlier call, or a shared link from a user.
2. **Launch** the (e.g., transcript, Markdown, or metadata) job and persist the `async_job_id`.
3. **Poll**`/check` with back-off until the job is `complete` or `failed`.
4. **On success**, take the result (e.g., transcript segments, Markdown string, or metadata) and store or process it in your own system.
5. **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.