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

# Custom deploy

> Push the artifact to your own endpoint when a build completes. Same mechanism that powers the StreamPixel integration.

If you have your own system that should receive new builds — a custom pixel-streaming runtime, a CDN, an internal artifact registry — set `autoDeploy: true` and point `uploadBuildUrl` at it.

## Configuration

```json theme={null}
{
  "autoDeploy": true,
  "uploadBuildUrl": "https://deploy.studio.com/builds/upload",
  "uploadBuildAuthHeader": "Bearer dt_..."
}
```

When the build completes successfully, BuildPixel POSTs the artifact to your URL.

## What gets sent

```http theme={null}
POST /builds/upload HTTP/1.1
Host: deploy.studio.com
Authorization: Bearer dt_...
Content-Type: multipart/form-data; boundary=...

--boundary
Content-Disposition: form-data; name="metadata"
Content-Type: application/json

{
  "buildId": "...",
  "projectId": "...",
  "platform": "Win64",
  "buildType": "Development",
  "ueVersion": "5.4",
  "branch": "main",
  "commit": "a1b2c3..."
}
--boundary
Content-Disposition: form-data; name="artifact"; filename="<buildId>.tar.gz"
Content-Type: application/gzip

<binary tarball bytes>
--boundary--
```

Two parts: a JSON metadata blob and the artifact tarball.

## Receiver examples

<CodeGroup>
  ```python Flask theme={null}
  @app.route("/builds/upload", methods=["POST"])
  def upload_build():
      metadata = json.loads(request.form["metadata"])
      artifact = request.files["artifact"]

      artifact.save(f"/srv/builds/{metadata['buildId']}.tar.gz")
      subprocess.run([
          "tar", "-xzf", f"/srv/builds/{metadata['buildId']}.tar.gz",
          "-C", "/srv/runtime/staging"
      ])

      runtime.deploy(metadata["buildId"])
      return "", 204
  ```

  ```python Steam upload theme={null}
  @app.route("/builds/upload", methods=["POST"])
  def upload_build():
      metadata = json.loads(request.form["metadata"])
      artifact = request.files["artifact"]

      artifact.save("staging.tar.gz")
      subprocess.run(["tar", "-xzf", "staging.tar.gz", "-C", "./staging"])
      subprocess.run([
          "steamcmd",
          "+login", os.environ["STEAM_USER"], os.environ["STEAM_PASS"],
          "+run_app_build", "app_build.vdf",
          "+quit"
      ])
      return "", 204
  ```

  ```python Internal artifact registry theme={null}
  @app.route("/builds/upload", methods=["POST"])
  def upload_build():
      metadata = json.loads(request.form["metadata"])
      artifact = request.files["artifact"]

      s3_key = f"builds/{metadata['projectId']}/{metadata['buildId']}.tar.gz"
      s3.upload_fileobj(artifact, "studio-artifacts", s3_key)

      db.execute(
          "INSERT INTO builds (id, project, platform, s3_key) VALUES (?, ?, ?, ?)",
          metadata["buildId"], metadata["projectId"],
          metadata["platform"], s3_key
      )
      return "", 204
  ```
</CodeGroup>

## If your endpoint fails

Failures don't fail the build — the artifact is still uploaded to BuildPixel storage and downloadable from the dashboard. The build's `autoDeployStatus` records whether the deploy succeeded.

## StreamPixel uses this same mechanism

The [StreamPixel integration](/artifacts/streampixel) is just a custom deploy with StreamPixel's URL plugged in. You can use the same approach to wire up any system that accepts a multipart upload.
