Layers
Partner APIAPI referenceMedia

POST /v1/projects/:projectId/media/finalize

Register an object-storage upload into the project's media library.

View as Markdown
POST/v1/projects/:projectId/media/finalize
Phase 1stable
Auth
Bearer
Scope
projects:write

Promotes a completed upload into the project's media library, returning a stable assetId (a.k.a. mediaId) you can reference from influencers, content, and ad creative. Call this immediately after the signed PUT succeeds — unfinalized uploads are garbage-collected after 24 hours.

Finalize verifies the object exists (via HEAD), checks the declared size matches, and inserts the row. It is safe to call more than once with the same uploadId — the first call creates the row, subsequent calls return the same asset.

Path
  • projectId
    string (uuid)required
    Project ID.
Body
  • uploadId
    stringrequired
    The `uploadId` returned by [`POST /media/presign`](/docs/api/reference/media/presign-upload).
  • checksumSha256
    string (64 hex)optional
    Optional sha256 to persist on the asset metadata.

Example request

curl -X POST https://api.layers.com/v1/projects/{projectId}/media/finalize \
  -H "X-Api-Key: $LAYERS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "uploadId": "upl_01HXA3KMNP4RSTUVWXYZABCDEF" }'
const res = await fetch(
  `https://api.layers.com/v1/projects/${projectId}/media/finalize`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': process.env.LAYERS_API_KEY!,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ uploadId }),
  },
);
const asset = await res.json();
import os, httpx

r = httpx.post(
    f"https://api.layers.com/v1/projects/{project_id}/media/finalize",
    headers={
        "X-Api-Key": os.environ["LAYERS_API_KEY"],
        "Content-Type": "application/json",
    },
    json={"uploadId": upload_id},
)
asset = r.json()

Response

200Asset row created (or echoed if re-finalized).
{
  "mediaId": "med_01HXA4MNP5RSTUVWXYZABCDEFGH",
  "assetId": "med_01HXA4MNP5RSTUVWXYZABCDEFGH",
  "url": "https://media.layers.com/.../founder-shot.jpg",
  "kind": "reference_image",
  "contentType": "image/jpeg",
  "mimeType": "image/jpeg",
  "byteSize": 482112,
  "sizeBytes": 482112,
  "filename": "founder-shot.jpg",
  "sha256": null,
  "checksumSha256": null,
  "createdAt": "2026-04-18T19:26:02Z"
}

Notes

  • mediaId and assetId are aliases. Same uuid, two keys — use either. Newer code paths (influencer reference-images, content assets) accept both.
  • contentType and mimeType are aliases. Same string, two keys.
  • byteSize and sizeBytes are aliases. Same number.
  • Re-finalize is a no-op. Calling finalize a second time with the same uploadId returns the existing row — use this for idempotent retries.

Errors

StatusCodeWhen
401UNAUTHENTICATEDMissing or invalid key.
403FORBIDDEN_SCOPEKey lacks projects:write.
404NOT_FOUNDProject or uploadId does not exist.
409UPLOAD_INCOMPLETEUpload object has not been PUT, or its size does not match the presign declaration.
410UPLOAD_EXPIREDuploadId is older than 24 hours. Call presign again.
422VALIDATION_FAILEDMalformed uploadId.
429RATE_LIMITEDWrite budget exhausted.

See also

On this page