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

# Join Jobs

> Stitch multiple video clips into a single output using the join job API

A join job stitches an ordered list of video clips into a single output file. Each clip can be a static video URL or a nested job definition that renders first. The whole pipeline - render child jobs, wait for them, stitch - is handled in one API call.

## How It Works

Submit a `POST /jobs/join` request with an ordered `assets` array. Each asset is either:

* `type: "video"` - a direct URL to an existing video file
* `type: "job"` - a job definition that Nexrender renders first, then uses the output

When job assets are present, Nexrender renders all child jobs in parallel, waits for them to complete, then stitches the outputs in the order you specified.

## Basic Example - Static Videos

The simplest case: stitch two existing video files together.

<CodeGroup>
  ```bash Request theme={null}
  curl -X POST https://api.nexrender.com/api/v2/jobs/join \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "assets": [
        {
          "type": "video",
          "src": "https://cdn.example.com/intro.mp4"
        },
        {
          "type": "video",
          "src": "https://cdn.example.com/outro.mp4"
        }
      ]
    }'
  ```

  ```json Response theme={null}
  {
    "id": "01JOIN_JOB_ID_HERE",
    "status": "queued",
    "outputUrl": "https://nx1-outputs-eu.nexrender.com/.../stitched.mp4"
  }
  ```
</CodeGroup>

## Mixed Example - Render and Stitch

Render a personalized middle segment from a template and stitch it between a static intro and outro.

<CodeGroup>
  ```bash Request theme={null}
  curl -X POST https://api.nexrender.com/api/v2/jobs/join \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "assets": [
        {
          "type": "video",
          "src": "https://cdn.example.com/intro.mp4"
        },
        {
          "type": "job",
          "template": {
            "id": "PERSONALIZED_TEMPLATE_ID",
            "composition": "main"
          },
          "assets": [
            {
              "type": "text",
              "layerName": "name",
              "value": "Sarah Chen"
            },
            {
              "type": "image",
              "layerName": "avatar",
              "src": "https://cdn.example.com/users/sarah.jpg"
            }
          ]
        },
        {
          "type": "video",
          "src": "https://cdn.example.com/outro.mp4"
        }
      ],
      "settings": {
        "preset": "mp4"
      },
      "webhook": {
        "url": "https://yourdomain.com/webhooks/stitch-complete"
      }
    }'
  ```

  ```json Response theme={null}
  {
    "id": "01JOIN_JOB_ID_HERE",
    "status": "pending",
    "outputUrl": "https://nx1-outputs-eu.nexrender.com/.../stitched.mp4",
    "children": [
      {
        "id": "01CHILD_JOB_ID_HERE",
        "status": "queued",
        "outputUrl": "https://nx1-outputs-eu.nexrender.com/.../middle.mp4"
      }
    ]
  }
  ```
</CodeGroup>

The parent enters `pending` while the child job renders. Once the child finishes, the stitch runs automatically and the parent transitions to `finished`.

## Request Fields

### Top-level

| Field             | Type   | Required | Description                                                            |
| ----------------- | ------ | -------- | ---------------------------------------------------------------------- |
| `assets`          | array  | Yes      | Ordered list of clips to stitch (minimum 1)                            |
| `settings.preset` | string | No       | Output format - currently `"mp4"`                                      |
| `upload`          | object | No       | Push output to your S3-compatible bucket (same schema as regular jobs) |
| `webhook`         | object | No       | Callback URL for status notifications                                  |

### Video Asset (`type: "video"`)

| Field  | Type   | Required | Description           |
| ------ | ------ | -------- | --------------------- |
| `type` | string | Yes      | Must be `"video"`     |
| `src`  | string | Yes      | URL to the video file |

### Job Asset (`type: "job"`)

| Field                  | Type    | Required | Description                                 |
| ---------------------- | ------- | -------- | ------------------------------------------- |
| `type`                 | string  | Yes      | Must be `"job"`                             |
| `template.id`          | string  | Yes      | Template ID for the child job               |
| `template.composition` | string  | Yes      | Composition to render                       |
| `assets`               | array   | No       | Assets to inject into the child job         |
| `preview`              | boolean | No       | Render child at preview quality             |
| `settings.quality`     | string  | No       | `"draft"` or `"full"`                       |
| `settings.codec`       | string  | No       | Output codec                                |
| `settings.engine`      | string  | No       | AE engine version: `"ae2025"` or `"ae2026"` |

## Tracking a Join Job

Track the join job with `GET /jobs/:id` as you would any other job. When all child jobs are rendering, the parent is `pending`. Once children complete and the stitch begins, the parent moves to `render:dorender`, then `finished`.

The `outputUrl` in the creation response is populated immediately and points to where the final stitched file will be available once the job is `finished`.

## When to Use Join Jobs

Join jobs are the right choice when you want to concatenate clips end-to-end:

* Intro + personalized body + outro for marketing emails or event invites
* Chapter-by-chapter assembly of a long-form video from separate renders
* Combining a rendered animation with pre-recorded footage clips

If you need to composite one render as a layer inside another composition (rather than appending clips), use a [Nested Job](/docs/cloud/jobs/nested_jobs) instead.

<CardGroup cols={2}>
  <Card title="Nested Jobs" icon="diagram-project" href="/docs/cloud/jobs/nested_jobs">
    Composite child renders as layers inside a parent composition
  </Card>

  <Card title="Batch Jobs" icon="layer-group" href="/docs/cloud/jobs/batch_jobs">
    Submit up to 1,000 jobs in a single request
  </Card>
</CardGroup>
