> ## Documentation Index
> Fetch the complete documentation index at: https://docs.raydocs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Synchronize Workflow Outputs

> Discover completed workflow runs reliably and download their persisted documents without importing pre-activation history.

Use the completed-run cursor API to copy workflow outputs into an external system without missing slow runs or downloading history from before activation.

## Prerequisites

Create a dedicated API token with the minimum abilities:

* `workspaces-read`
* `workflows-read`
* `workflow-runs-output-read`
* `documents-read`

In the API token modal, the **Raydocs Windows sync client** preset selects exactly these abilities and scopes the token to the current workspace by default.

The token owner must be an administrator of every workflow workspace the integration follows. Do not grant `workflow-agent` and do not send workflow-agent headers.

Your workflow must finish with `files.persistWorkspace`. Its public output must expose the resulting `documents` array described in [Get Workflow Run Output](/api-reference/workflows/get-workflow-run-output).

## Activate a workflow feed

Send one bootstrap request in cursor mode. Ignore its runs, then store its `meta.server_time` as `enabled_at` and its `meta.next_cursor` as the initial cursor.

```bash theme={null}
curl --get "https://api.raydocs.com/workspaces/12/workflows/7d3a20e1-1e99-4eb0-9ab7-39ee5028caf4/runs" \
  --header "Authorization: Bearer $RAYDOCS_API_TOKEN" \
  --data-urlencode "pagination=cursor" \
  --data-urlencode "status=completed" \
  --data-urlencode "created_after=1970-01-01T00:00:00Z" \
  --data-urlencode "per_page=100"
```

For every later poll, keep `created_after` equal to the stored `enabled_at`. This excludes runs created before activation while still discovering runs created after activation that take hours or days to complete.

## Process one discovery page

For each returned run:

1. Request the run's public output.
2. Validate that `documents` is an array and every entry contains `documentId` and `filename`.
3. Build the idempotency key `workflow_id:run_id:documentId`.
4. Store one download event per document.
5. Commit the events and `meta.next_cursor` together.
6. Request the next page when `meta.has_more=true`.

If one run has an invalid output, roll back the page and retry it later. A document download failure does not invalidate discovery; keep the event and retry the download independently.

Follow pages while `meta.has_more=true`. After the last page, keep polling with its returned cursor; runs that complete later appear after the stored `(completed_at, id)` checkpoint.

## Download a document

Request a fresh signed URL with the document's `documentId`, then stream the response to a temporary local file.

```bash theme={null}
SIGNED_URL=$(curl --silent \
  "https://api.raydocs.com/workspaces/12/documents/880e8400-e29b-41d4-a716-446655440000/url" \
  --header "Authorization: Bearer $RAYDOCS_API_TOKEN" | jq -r .url)

curl --fail --location "$SIGNED_URL" --output export.zip.partial
```

Before exposing the final file, verify `size` and `sha256` when present, flush the file to disk, and rename it atomically on the same volume. Request a new signed URL after expiration; never store a signed URL as durable state.

## Reliability rules

* Keep `enabled_at` immutable.
* Treat the cursor as opaque.
* Store the page cursor only after its events are durable.
* Use the `workflow_id:run_id:documentId` idempotency key to absorb client or network retries safely.
* Never advance past an invalid run output.
* Never use temporary workflow `fileRef` download routes for persisted documents.
