Nubu
  • Features
  • How it works
  • Pricing
  • Integrations
  • FAQ
  • Contact
  • Docs
  • Blog
  • Log in
  • Log inSign up
    Nubu

    Creative automation for teams that ship campaigns, not busywork.

    Try for free
    Product
    FeaturesNubu MotionHow it worksPricingIntegrationsFAQ
    Company
    AboutContactDocsBlogLog in
    Legal
    Privacy policyTerms of serviceData deletion
    © 2026 Bear Studios · Nubu
    Introduction
    Templates overviewInstalling Nubu BuilderAdding template compsAdding propertiesAdding footageSetting defaultsExporting your templateUploading a template
    Flows overviewAvailable nodesAdding a nodeConnecting nodesWorking with dataTemplates and outputsUsing AI nodesBuilding a creative
    CampaignsTemplatesAssetsGlossariesRendersSettings
    Connecting Meta AdsConnecting Google AdsConnecting OpenAIConnecting Gemini AI StudioConnecting Anthropic
    AI Assistant overviewPrompt examplesAssistant settings and controls
    Nubu Motion scripts: the reference
    OverviewQuick startDifferences from After EffectsDeterminism
    JavaScript supportReferences and idsProject scriptsState and timeBudgets and errorsFormatting values
    GlobalTime conversionInterpolationVector mathsRandom and noiseColour conversionOther maths
    LayerCompPropertyKeyframePathGeometryProjectComponentConsoleComponentControlExpressionControl
    The property vocabularyAnimators propertiesAudio propertiesContent propertiesContents propertiesEffects propertiesExpression controls propertiesFills propertiesInstance controls propertiesLayer propertiesLayout propertiesMasks propertiesStrokes propertiesText style propertiesRig propertiesRigging propertiesRig controllers propertiesRigging weights propertiesRigging mesh propertiesRig items propertiesRig splines propertiesRig simulation propertiesRig properties propertiesRig drivers propertiesRig draw order propertiesTransform propertiesTrim path properties
    Error codesBudgets
    Changelog
    API overviewAuthentication and keysRate limits and errorsUploading assetsFoldersUploading templatesTriggering a flowRuns and creativesAPI Request deliveryCallbacks
    All docs
    API

    Uploading assets

    Upload files into the library through presigned URLs, in one PUT or in parts, then manage them.

    Assets uploaded through the API are ordinary library assets: they count towards the organisation's storage, follow the same format rules and limits as the Assets page, get the same previews and duplicate detection, and are what you hand to a flow's API Trigger.

    The upload flow#

    An upload has three steps. The bytes travel straight to storage, so a 100 GB file works exactly as it does from the browser.

    1. Begin#

    curl -X POST https://app.nubu.app/api/v1/assets/uploads \
      -H "Authorization: Bearer nubu_api_..." \
      -H "Content-Type: application/json" \
      -d '{ "filename": "hero.jpg", "size_bytes": 348211, "content_type": "image/jpeg", "folder_id": null }'
    

    The answer (201) carries the new asset_id and an upload plan:

    • "mode": "single": one url to PUT the whole file to, valid for expires_in_seconds (an hour).
    • "mode": "multipart" (files over 100 MiB): an upload_id, the part_size_bytes (64 MiB), the part_count, and parts, the first hundred presigned part URLs.

    The kind of asset follows the filename's extension. When an extension fits more than one kind (a .pdf can be an image or a document), pass "kind": "image" or "kind": "document". Pass "name" to give the asset a library name other than the filename.

    Pass "folder_id" (a folder's id from Folders) to upload straight into a folder; omit it or pass null for the library's root. A folder that is not the organisation's answers 422 validation_failed.

    2. PUT the bytes#

    Single mode: PUT the file to url with the same Content-Type you declared.

    curl -X PUT "<url>" -H "Content-Type: image/jpeg" --data-binary @hero.jpg
    

    Multipart mode: split the file into part_size_bytes pieces (the last one takes the remainder) and PUT each piece to its part URL with no Content-Type. Parts may go up in any order and in parallel. A part URL is valid for an hour; for the parts you have not sent yet, or for a retry, ask for fresh URLs:

    curl -X POST https://app.nubu.app/api/v1/assets/uploads/<asset_id>/parts \
      -H "Authorization: Bearer nubu_api_..." \
      -H "Content-Type: application/json" \
      -d '{ "upload_id": "<upload_id>", "part_numbers": [101, 102, 103] }'
    

    3. Complete#

    curl -X POST https://app.nubu.app/api/v1/assets/uploads/<asset_id>/complete \
      -H "Authorization: Bearer nubu_api_..." \
      -H "Content-Type: application/json" \
      -d '{ "upload_id": "<upload_id>", "width": 1920, "height": 1080, "sha256": "<hex>" }'
    

    Send upload_id only for a multipart upload. width, height, duration_seconds and frame_rate are optional and improve how the asset shows in the library; sha256 (the hex digest of the bytes) lets Nubu tell you about an identical file already in the library (duplicate_of). The answer carries the ready asset. Completing an asset that is already complete answers it again, so a retry is safe.

    If the bytes have not arrived yet the complete answers 409 conflict; to abandon an upload, POST /assets/uploads/<asset_id>/abort (with the upload_id for a multipart one) removes the pending asset and anything stored for it.

    Several files at once#

    POST /assets/uploads/batch takes { "files": [ ... ] } (up to 50 entries shaped like a single begin) and answers one entry per file, in order, with either its asset_id and upload plan or an error. Complete each file on its own afterwards.

    Storage is checked at the begin, before any URL is handed out: a file that would not fit the organisation's remaining allowance answers 409 conflict with details.reason storage_full and a message saying how much is free (in a batch, that file's entry carries the error and the others proceed; a replacement is checked the same way, with the old file's size credited). Deleting assets frees the space at once. See Rate limits and errors.

    Reading the library#

    • GET /assets lists assets, newest first. Filters: folder_id (a folder's id; see Folders), kind (image, video, audio, document), status (pending, ready, failed), q (a name search) and tag.
    • GET /assets/<id> answers the asset with a download_url for the file, valid for 15 minutes.

    Editing, replacing and deleting#

    • PATCH /assets/<id> with any of name, tags (an array) and folder_id (null for the root).
    • Replacing a file keeps the asset id and everything that references it. POST /assets/<id>/replace with filename, size_bytes and content_type (the extension must match the asset's) answers an upload plan and a file_key; PUT the bytes, then POST /assets/<id>/replace/complete with that file_key (and the upload_id for a multipart one). Until the complete succeeds the original stays in place. POST /assets/<id>/replace/parts re-mints part URLs and POST /assets/<id>/replace/abort discards the attempt.
    • DELETE /assets/<id> removes the asset and its files. An asset used by a creative that is queued or rendering answers 409 conflict until that render finishes.