Rendley docs

API reference

Uploads

Project-level media. Add files to a single project, list what it holds, and use multipart uploads when a file is too large to send in one request.

#List uploads

GEThttps://api.rendley.com/v1/projects/{projectId}/uploads

List uploads for a project, or get a single upload by media_id (or file hash). An optional role filter scopes the list to one lifecycle role (the editor's hash sync passes role=library so pending/transient files are invisible to its reconciliation; the pending poller passes role=pending).

Don't have one? Create an API key

const res = await fetch("https://api.rendley.com/v1/projects/PROJECT_ID/uploads", {
  headers: { Authorization: "Bearer YOUR_API_KEY" },
});
const { data } = await res.json();
curl "https://api.rendley.com/v1/projects/PROJECT_ID/uploads" \
  -H "Authorization: Bearer YOUR_API_KEY"
import requests

res = requests.get(
    "https://api.rendley.com/v1/projects/PROJECT_ID/uploads",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
data = res.json()["data"]
$ch = curl_init("https://api.rendley.com/v1/projects/PROJECT_ID/uploads");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_API_KEY"]);

$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true)["data"];
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.rendley.com/v1/projects/PROJECT_ID/uploads", nil)
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	b, _ := io.ReadAll(res.Body)
	fmt.Println(string(b))
}
require "net/http"
require "json"

uri = URI("https://api.rendley.com/v1/projects/PROJECT_ID/uploads")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)["data"]

Response 200

{
  "data": [
    {
      "duration": 0,
      "file_hash": "c6c8ec4f9a6fdd9d",
      "media_id": "c448b6e3-2b78-4bda-b369-d1afc6aec07f",
      "mime_type": "string",
      "original_file_name": "string",
      "role": "string",
      "status": "string",
      "storage_url": "string"
    }
  ]
}

#Create an upload

POSThttps://api.rendley.com/v1/projects/{projectId}/uploads

Register a file and get a presigned URL to PUT its bytes to, then call the complete endpoint. media_id and file_hash are optional for API keys: the server generates the media_id (returned in the response) and records a placeholder hash. Use the media_id as the media reference of AI actions once the upload is complete.

Don't have one? Create an API key

Body

FileHash is the client-computed XXH64 content hash (16 hex chars). Optional for API keys.

MediaId is the client-chosen media id (a UUID). Optional for API keys.

Role is the sync-lifecycle tag: "library" (reconciled by the editor's hash sync), "pending" (awaiting auto-injection into the library; invisible to the sync), "staged" (agent/AI workflow file; invisible to the sync AND not auto-injected: adopted to library only when explicitly referenced) or "transient" (never a library asset). Defaults to "library"; "pending" when Source is "mcp"; "staged" for API-key requests (so opening the project in the editor never reaps an API-uploaded file before it is referenced).

Source is provenance: which flow created the upload.

const res = await fetch("https://api.rendley.com/v1/projects/PROJECT_ID/uploads", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    "duration": 0,
    "file_hash": "<file_hash>",
    "file_size": 0
  }),
});
const { data } = await res.json();
curl -X POST "https://api.rendley.com/v1/projects/PROJECT_ID/uploads" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "duration": 0,
    "file_hash": "<file_hash>",
    "file_size": 0
  }'
import requests

res = requests.post(
    "https://api.rendley.com/v1/projects/PROJECT_ID/uploads",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "duration": 0,
        "file_hash": "<file_hash>",
        "file_size": 0
    },
)
data = res.json()["data"]
$ch = curl_init("https://api.rendley.com/v1/projects/PROJECT_ID/uploads");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_API_KEY", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["duration" => 0, "file_hash" => "<file_hash>", "file_size" => 0]));

$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true)["data"];
package main

import (
	"fmt"
	"io"
	"net/http"
	"bytes"
)

func main() {
	body := bytes.NewBufferString(`{
  "duration": 0,
  "file_hash": "<file_hash>",
  "file_size": 0
}`)
	req, _ := http.NewRequest("POST", "https://api.rendley.com/v1/projects/PROJECT_ID/uploads", body)
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	req.Header.Set("Content-Type", "application/json")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	b, _ := io.ReadAll(res.Body)
	fmt.Println(string(b))
}
require "net/http"
require "json"

uri = URI("https://api.rendley.com/v1/projects/PROJECT_ID/uploads")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"
req["Content-Type"] = "application/json"
req.body = { duration: 0, file_hash: "<file_hash>", file_size: 0 }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)["data"]

Response 200

{
  "data": {
    "media_id": "c448b6e3-2b78-4bda-b369-d1afc6aec07f",
    "presigned_url": "string",
    "upload_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70"
  }
}

#Delete an upload

Delete a project upload by its media ID.

DELETEhttps://api.rendley.com/v1/projects/{projectId}/uploads/{mediaId}

Don't have one? Create an API key

const res = await fetch("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/MEDIA_ID", {
  method: "DELETE",
  headers: { Authorization: "Bearer YOUR_API_KEY" },
});
const { data } = await res.json();
curl -X DELETE "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/MEDIA_ID" \
  -H "Authorization: Bearer YOUR_API_KEY"
import requests

res = requests.delete(
    "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/MEDIA_ID",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
data = res.json()["data"]
$ch = curl_init("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/MEDIA_ID");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_API_KEY"]);

$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true)["data"];
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("DELETE", "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/MEDIA_ID", nil)
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	b, _ := io.ReadAll(res.Body)
	fmt.Println(string(b))
}
require "net/http"
require "json"

uri = URI("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/MEDIA_ID")
req = Net::HTTP::Delete.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)["data"]

#Complete an upload

POSThttps://api.rendley.com/v1/projects/{projectId}/uploads/{uploadId}/complete

Mark a single upload as completed by its ID once the bytes have been PUT to the presigned URL. Returns the completed upload, including the media_id to use as the media reference of AI actions.

Don't have one? Create an API key

const res = await fetch("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/UPLOAD_ID/complete", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_API_KEY" },
});
const { data } = await res.json();
curl -X POST "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/UPLOAD_ID/complete" \
  -H "Authorization: Bearer YOUR_API_KEY"
import requests

res = requests.post(
    "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/UPLOAD_ID/complete",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
data = res.json()["data"]
$ch = curl_init("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/UPLOAD_ID/complete");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_API_KEY"]);

$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true)["data"];
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/UPLOAD_ID/complete", nil)
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	b, _ := io.ReadAll(res.Body)
	fmt.Println(string(b))
}
require "net/http"
require "json"

uri = URI("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/UPLOAD_ID/complete")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)["data"]

Response 200

{
  "data": {
    "duration": 0,
    "file_hash": "c6c8ec4f9a6fdd9d",
    "media_id": "c448b6e3-2b78-4bda-b369-d1afc6aec07f",
    "mime_type": "string",
    "original_file_name": "string",
    "role": "string",
    "status": "string",
    "storage_url": "string"
  }
}

#Create a batch of uploads

Create multiple uploads at once, returning presigned URLs for accepted files and reasons for rejected ones.

POSThttps://api.rendley.com/v1/projects/{projectId}/uploads/batch

Don't have one? Create an API key

Body
filesrequiredobject[]

FileHash is the client-computed XXH64 content hash (16 hex chars). Optional for API keys.

MediaId is the client-chosen media id (a UUID). Optional for API keys.

Role is the sync-lifecycle tag: "library" (reconciled by the editor's hash sync), "pending" (awaiting auto-injection into the library; invisible to the sync), "staged" (agent/AI workflow file; invisible to the sync AND not auto-injected: adopted to library only when explicitly referenced) or "transient" (never a library asset). Defaults to "library"; "pending" when Source is "mcp"; "staged" for API-key requests (so opening the project in the editor never reaps an API-uploaded file before it is referenced).

Source is provenance: which flow created the upload.

const res = await fetch("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    "files": [
      {
        "duration": 0,
        "file_hash": "c6c8ec4f9a6fdd9d",
        "file_size": 0,
        "media_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70",
        "metadata": "string",
        "mime_type": "string",
        "original_file_name": "string",
        "project_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70",
        "role": "library",
        "source": "editor"
      }
    ]
  }),
});
const { data } = await res.json();
curl -X POST "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "files": [
      {
        "duration": 0,
        "file_hash": "c6c8ec4f9a6fdd9d",
        "file_size": 0,
        "media_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70",
        "metadata": "string",
        "mime_type": "string",
        "original_file_name": "string",
        "project_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70",
        "role": "library",
        "source": "editor"
      }
    ]
  }'
import requests

res = requests.post(
    "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "files": [
            {
                "duration": 0,
                "file_hash": "c6c8ec4f9a6fdd9d",
                "file_size": 0,
                "media_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70",
                "metadata": "string",
                "mime_type": "string",
                "original_file_name": "string",
                "project_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70",
                "role": "library",
                "source": "editor"
            }
        ]
    },
)
data = res.json()["data"]
$ch = curl_init("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_API_KEY", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["files" => [["duration" => 0, "file_hash" => "c6c8ec4f9a6fdd9d", "file_size" => 0, "media_id" => "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70", "metadata" => "string", "mime_type" => "string", "original_file_name" => "string", "project_id" => "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70", "role" => "library", "source" => "editor"]]]));

$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true)["data"];
package main

import (
	"fmt"
	"io"
	"net/http"
	"bytes"
)

func main() {
	body := bytes.NewBufferString(`{
  "files": [
    {
      "duration": 0,
      "file_hash": "c6c8ec4f9a6fdd9d",
      "file_size": 0,
      "media_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70",
      "metadata": "string",
      "mime_type": "string",
      "original_file_name": "string",
      "project_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70",
      "role": "library",
      "source": "editor"
    }
  ]
}`)
	req, _ := http.NewRequest("POST", "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch", body)
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	req.Header.Set("Content-Type", "application/json")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	b, _ := io.ReadAll(res.Body)
	fmt.Println(string(b))
}
require "net/http"
require "json"

uri = URI("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"
req["Content-Type"] = "application/json"
req.body = { files: [{ duration: 0, file_hash: "c6c8ec4f9a6fdd9d", file_size: 0, media_id: "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70", metadata: "string", mime_type: "string", original_file_name: "string", project_id: "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70", role: "library", source: "editor" }] }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)["data"]

Response 200

{
  "data": {
    "items": [
      {
        "media_id": "c448b6e3-2b78-4bda-b369-d1afc6aec07f",
        "presigned_url": "string",
        "upload_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70"
      }
    ],
    "rejected": [
      {
        "media_id": "c448b6e3-2b78-4bda-b369-d1afc6aec07f",
        "reason": "string"
      }
    ]
  }
}

#Complete a batch of uploads

Mark a batch of uploads as completed by their upload IDs.

POSThttps://api.rendley.com/v1/projects/{projectId}/uploads/batch/complete

Don't have one? Create an API key

Body
const res = await fetch("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch/complete", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    "upload_ids": [
      "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70"
    ]
  }),
});
const { data } = await res.json();
curl -X POST "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch/complete" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "upload_ids": [
      "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70"
    ]
  }'
import requests

res = requests.post(
    "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch/complete",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "upload_ids": [
            "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70"
        ]
    },
)
data = res.json()["data"]
$ch = curl_init("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch/complete");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_API_KEY", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["upload_ids" => ["3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70"]]));

$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true)["data"];
package main

import (
	"fmt"
	"io"
	"net/http"
	"bytes"
)

func main() {
	body := bytes.NewBufferString(`{
  "upload_ids": [
    "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70"
  ]
}`)
	req, _ := http.NewRequest("POST", "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch/complete", body)
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	req.Header.Set("Content-Type", "application/json")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	b, _ := io.ReadAll(res.Body)
	fmt.Println(string(b))
}
require "net/http"
require "json"

uri = URI("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/batch/complete")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"
req["Content-Type"] = "application/json"
req.body = { upload_ids: ["3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70"] }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)["data"]

Response 200

{
  "data": {
    "completed": 0,
    "failed": [
      {
        "error": "string",
        "upload_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70"
      }
    ]
  }
}

#Import an upload from a URL

POSThttps://api.rendley.com/v1/projects/{projectId}/uploads/import

One-call upload for a file that is already hosted: the server downloads it from a public HTTPS direct link, stores it and returns the completed upload. Use the returned media_id as the media reference of AI actions. No client-side hashing or presigned PUT is needed. The host must report Content-Length; files are capped at 5 GB.

Don't have one? Create an API key

Body

Role/Source follow the same defaults as UploadInput.

const res = await fetch("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/import", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    "download_url": "<download_url>"
  }),
});
const { data } = await res.json();
curl -X POST "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/import" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "download_url": "<download_url>"
  }'
import requests

res = requests.post(
    "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/import",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "download_url": "<download_url>"
    },
)
data = res.json()["data"]
$ch = curl_init("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/import");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_API_KEY", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["download_url" => "<download_url>"]));

$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true)["data"];
package main

import (
	"fmt"
	"io"
	"net/http"
	"bytes"
)

func main() {
	body := bytes.NewBufferString(`{
  "download_url": "<download_url>"
}`)
	req, _ := http.NewRequest("POST", "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/import", body)
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	req.Header.Set("Content-Type", "application/json")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	b, _ := io.ReadAll(res.Body)
	fmt.Println(string(b))
}
require "net/http"
require "json"

uri = URI("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/import")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"
req["Content-Type"] = "application/json"
req.body = { download_url: "<download_url>" }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)["data"]

Response 200

{
  "data": {
    "duration": 0,
    "file_hash": "c6c8ec4f9a6fdd9d",
    "media_id": "c448b6e3-2b78-4bda-b369-d1afc6aec07f",
    "mime_type": "string",
    "original_file_name": "string",
    "role": "string",
    "status": "string",
    "storage_url": "string"
  }
}

#Abort a multipart upload

Abort an in-progress multipart upload.

POSThttps://api.rendley.com/v1/projects/{projectId}/uploads/multipart/abort

Don't have one? Create an API key

Body
const res = await fetch("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/abort", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    "s3_upload_id": "<s3_upload_id>",
    "upload_id": "<upload_id>"
  }),
});
const { data } = await res.json();
curl -X POST "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/abort" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "s3_upload_id": "<s3_upload_id>",
    "upload_id": "<upload_id>"
  }'
import requests

res = requests.post(
    "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/abort",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "s3_upload_id": "<s3_upload_id>",
        "upload_id": "<upload_id>"
    },
)
data = res.json()["data"]
$ch = curl_init("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/abort");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_API_KEY", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["s3_upload_id" => "<s3_upload_id>", "upload_id" => "<upload_id>"]));

$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true)["data"];
package main

import (
	"fmt"
	"io"
	"net/http"
	"bytes"
)

func main() {
	body := bytes.NewBufferString(`{
  "s3_upload_id": "<s3_upload_id>",
  "upload_id": "<upload_id>"
}`)
	req, _ := http.NewRequest("POST", "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/abort", body)
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	req.Header.Set("Content-Type", "application/json")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	b, _ := io.ReadAll(res.Body)
	fmt.Println(string(b))
}
require "net/http"
require "json"

uri = URI("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/abort")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"
req["Content-Type"] = "application/json"
req.body = { s3_upload_id: "<s3_upload_id>", upload_id: "<upload_id>" }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)["data"]

#Complete a multipart upload

Complete a multipart upload by submitting the uploaded parts.

POSThttps://api.rendley.com/v1/projects/{projectId}/uploads/multipart/complete

Don't have one? Create an API key

Body
partsrequiredobject[]
const res = await fetch("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/complete", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    "parts": [
      {
        "etag": "string",
        "part_number": 0
      }
    ],
    "s3_upload_id": "<s3_upload_id>",
    "upload_id": "<upload_id>"
  }),
});
const { data } = await res.json();
curl -X POST "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/complete" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "parts": [
      {
        "etag": "string",
        "part_number": 0
      }
    ],
    "s3_upload_id": "<s3_upload_id>",
    "upload_id": "<upload_id>"
  }'
import requests

res = requests.post(
    "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/complete",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "parts": [
            {
                "etag": "string",
                "part_number": 0
            }
        ],
        "s3_upload_id": "<s3_upload_id>",
        "upload_id": "<upload_id>"
    },
)
data = res.json()["data"]
$ch = curl_init("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/complete");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_API_KEY", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["parts" => [["etag" => "string", "part_number" => 0]], "s3_upload_id" => "<s3_upload_id>", "upload_id" => "<upload_id>"]));

$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true)["data"];
package main

import (
	"fmt"
	"io"
	"net/http"
	"bytes"
)

func main() {
	body := bytes.NewBufferString(`{
  "parts": [
    {
      "etag": "string",
      "part_number": 0
    }
  ],
  "s3_upload_id": "<s3_upload_id>",
  "upload_id": "<upload_id>"
}`)
	req, _ := http.NewRequest("POST", "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/complete", body)
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	req.Header.Set("Content-Type", "application/json")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	b, _ := io.ReadAll(res.Body)
	fmt.Println(string(b))
}
require "net/http"
require "json"

uri = URI("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/complete")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"
req["Content-Type"] = "application/json"
req.body = { parts: [{ etag: "string", part_number: 0 }], s3_upload_id: "<s3_upload_id>", upload_id: "<upload_id>" }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)["data"]

#Initiate a multipart upload

POSThttps://api.rendley.com/v1/projects/{projectId}/uploads/multipart/initiate

For large files: register the file and get presigned URLs for each part, PUT the parts, then call the multipart complete endpoint with their ETags. media_id and file_hash are optional for API keys (see the create upload endpoint); the response carries the media_id.

Don't have one? Create an API key

Body

FileHash is the client-computed XXH64 content hash (16 hex chars). Optional for API keys.

MediaId is the client-chosen media id (a UUID). Optional for API keys.

Role is the sync-lifecycle tag: "library" (reconciled by the editor's hash sync), "pending" (awaiting auto-injection into the library; invisible to the sync), "staged" (agent/AI workflow file; invisible to the sync AND not auto-injected: adopted to library only when explicitly referenced) or "transient" (never a library asset). Defaults to "library"; "pending" when Source is "mcp"; "staged" for API-key requests (so opening the project in the editor never reaps an API-uploaded file before it is referenced).

Source is provenance: which flow created the upload.

const res = await fetch("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/initiate", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    "part_count": 0
  }),
});
const { data } = await res.json();
curl -X POST "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/initiate" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "part_count": 0
  }'
import requests

res = requests.post(
    "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/initiate",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "part_count": 0
    },
)
data = res.json()["data"]
$ch = curl_init("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/initiate");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_API_KEY", "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["part_count" => 0]));

$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true)["data"];
package main

import (
	"fmt"
	"io"
	"net/http"
	"bytes"
)

func main() {
	body := bytes.NewBufferString(`{
  "part_count": 0
}`)
	req, _ := http.NewRequest("POST", "https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/initiate", body)
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	req.Header.Set("Content-Type", "application/json")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	b, _ := io.ReadAll(res.Body)
	fmt.Println(string(b))
}
require "net/http"
require "json"

uri = URI("https://api.rendley.com/v1/projects/PROJECT_ID/uploads/multipart/initiate")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"
req["Content-Type"] = "application/json"
req.body = { part_count: 0 }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)["data"]

Response 200

{
  "data": {
    "media_id": "c448b6e3-2b78-4bda-b369-d1afc6aec07f",
    "presigned_urls": [
      {
        "part_number": 0,
        "url": "string"
      }
    ],
    "s3_upload_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70",
    "upload_id": "3f0d5a4e-3d2a-4c1f-9b3e-2a1c4d5e6f70"
  }
}