Files

Update file metadata

Updates the metadata field of a file

patch/v1/projects/{project_id}/files/{file_id}/metadata

Request

  • Base URL: https://api.naturali.ai
  • URL: https://api.naturali.ai/v1/projects/{project_id}/files/{file_id}/metadata
  • Auth: one of:
    • HTTP bearer
    • OAuth 2 (authorization code, authorization URL https://api.naturali.ai/authorize, token URL https://api.naturali.ai/token, scopes: mcp:access)

Path parameters

file_idstring required
Example:file_abc123

File ID

Request body

metadataNullableMetadataBag nullable

A MetadataBag on a request field where null is meaningful: a full-replacement update that clears the bag. A record whose bag is unset leaves the field out.

prefixstring

New directory — moves the file. The resulting path (prefix + filename) must be unique within the project.

filenamestring

New filename — renames the key's leaf and the download name.

Example request

{
  "metadata": {
    "author": "John",
    "revision": 2
  },
  "prefix": "/reports",
  "filename": "renamed-file.txt"
}

Response

Metadata updated successfully

idstring

Unique file identifier

prefixstring

Directory of the file (the path without its last segment). Read-only — set it via prefix on write.

filenamestring

Original / download name and the key's leaf segment.

pathstring nullable

Full key of the file within the project — prefix + / + filename (e.g. /images/logo.png). Read-only; unique per project; the file's identity and policy-SRN target.

content_typestring nullable

MIME type of the file

sizeinteger nullable

File size in bytes

metadataMetadataBag

Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.

No key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.

tagsTagBag

Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with 400 VALIDATION_FAILED, never coerced. Keys are opaque and stored verbatim, so cost_center and costCenter are two different tags. Matched by JSONB containment wherever tags are read: the ?tags= filter and knowledge search.

Keys beginning system. are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with 400 RESERVED_TAG_KEY. They are read and filtered like any other tag.

The bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is 400 VALIDATION_FAILED with meta.limit naming the bound it crossed. system.* keys are the platform's and do not count against the 50.

created_atstring date-time

Creation timestamp

updated_atstring date-time

Last update timestamp

Example response

{
  "id": "abc123",
  "prefix": "/images",
  "filename": "logo.png",
  "path": "/images/logo.png",
  "content_type": "application/pdf",
  "size": 1024,
  "metadata": {
    "author": "John",
    "revision": 2
  },
  "tags": {
    "team": "finance",
    "env": "prod"
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.