# Getting results (/guides/results)



A submit answers when the job is accepted, not done. You wait on it, then fetch a short-lived link.

## Wait for it [#wait-for-it]

`GET /generations/{id}/wait` blocks up to 20 seconds. It answers the moment the job ends. Still going? It returns `still_running: true`. Call again right away.

<Tabs items="['REST','MCP','CLI']">
  <Tab value="REST">
    ```ts
    const BASE = "https://app.riffads.com/api/v1";
    const headers = { Authorization: `Bearer ${process.env.RIFFADS_API_KEY}` };

    async function waitFor(generationId: string, maxAgeSeconds = 1800) {
      for (;;) {
        const res = await fetch(`${BASE}/generations/${generationId}/wait`, { headers });
        const body = await res.json();
        if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
        if (!body.still_running) return body.generation;
        if (body.age_seconds > maxAgeSeconds) {
          throw new Error(`${generationId} still running after ${body.age_seconds}s`);
        }
      }
    }
    ```
  </Tab>

  <Tab value="MCP">
    ```text
    wait_for_generation { generation_id: "gen_..." }   // repeat until still_running is false
    get_generation      { generation_id: "gen_..." }   // one read, now
    get_batch           { batch_group_id: "bg_..." }   // every variant of one submit
    ```

    Each file also comes back as a `resource_link` block.
  </Tab>

  <Tab value="CLI">
    <SurfaceStatus id="cli" />

    ```bash title="Terminal"
    riffads status gen_... --wait        # block until done
    riffads download gen_... -o ./out    # save every output
    ```
  </Tab>
</Tabs>

* **Branch on `still_running`, not `status`.** It can end before `status` looks final.
* A timeout is a normal `200`, never `408` or `504`.
* Log `age_seconds`. It tells stuck from slow.

## Statuses [#statuses]

| Status            | Means                                                  | Done? |
| ----------------- | ------------------------------------------------------ | ----- |
| `queued`          | Accepted, not started                                  | No    |
| `rendering`       | Working                                                | No    |
| `post_processing` | Saving the output                                      | No    |
| `completed`       | Every output delivered                                 | Yes   |
| `failed`          | Did not deliver. Read `error.code` and `error.message` | Yes   |

There is no `canceled` status. A canceled job reads `failed`. More: [statuses and ids](/reference/statuses-and-ids).

## Variants [#variants]

Sent `variants: 3`? The submit returns a `batch_group_id` (`bg_`). Watching one id is not enough. Wait on one sibling, then read `GET /batches/{id}`. Batch status is `running`, `completed`, `failed` or `partial`.

## Signed links [#signed-links]

Every output sits in `generation.outputs[]`:

| Field             | Notes                                                           |
| ----------------- | --------------------------------------------------------------- |
| `index`           | Stable position. Name files after it                            |
| `kind`            | `image`, `video` or `audio`                                     |
| `url`             | Signed link, or `null` until the job is `completed` and settled |
| `width`, `height` | Pixels, or `null`                                               |

* Links live **600 seconds** (`output_urls_expire_in_seconds`).
* There is no refresh endpoint. Every read signs new links. Expired? Read the generation again.
* A link is a credential. Anyone holding it gets the file. Don't store it, log it or queue it.
* Compare `outputs_delivered` to `outputs_expected`, not the array length.
* Text capabilities (AI Writer, Transcribe) return no file.

## Download [#download]

```bash title="Terminal"
BASE=https://app.riffads.com/api/v1
GEN=gen_0193c8f0a1b24e7f9d3c5a6b7e8f0033

curl -s "$BASE/generations/$GEN" -H "Authorization: Bearer $RIFFADS_API_KEY" \
  | jq -r '.generation.outputs[] | select(.url != null) | [.index, .url] | @tsv' \
  | while IFS=$'\t' read -r index url; do
      curl -sS --fail -o "$GEN-$index" "$url"
    done
```

<Callout type="warn" title="A 403 on download usually means the link expired">
  Don't retry the same URL. Read the generation again for a fresh link, then fetch.
</Callout>

Fetch each link right after the read that signed it. Name saved files from `generation_id`, `index` and `kind`, not `file_name`. The download has no `Content-Disposition`, so browsers play it instead of saving it.

## Webhooks [#webhooks]

There are none. No `webhook_url`, no callback, no stream. Sending a callback field is a `400`. Use the wait loop.

Keep your `generation_id`. There is no list endpoint, so it is your only way back to the file.
