File Custom Field

Attach uploaded files to records with a file custom field, then read them back via the field's files list.


A file custom field attaches one or more uploaded files to a record. This is the FILE value of the CustomFieldType enum. Records are Record objects and workspaces are Workspace objects in the API.

Unlike most field types, file values are not written with setRecordCustomField. The flow is three steps: upload the file with uploadFile to get a file uid, attach that file to the record’s field with createRecordCustomFieldFile, then read the attached files back from the field’s files list.

Overview

Create the field with createCustomField using type: FILE. The field is scoped to the workspace in your X-Bloo-Project-ID header — there is no projectId parameter on the input. A single file field can hold multiple files; each is attached with its own createRecordCustomFieldFile call.

Create

mutation CreateFileField {
  createCustomField(input: { name: "Attachments", type: FILE }) {
    id
    name
    type
  }
}

CreateCustomFieldInput

ParameterTypeRequiredDescription
nameString!YesDisplay name of the field.
typeCustomFieldType!YesMust be FILE.
descriptionStringNoHelp text shown to users.

The field is associated with the workspace in your blue-workspace-id header. CreateCustomFieldInput has no projectId field.

{
  "data": {
    "createCustomField": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "name": "Attachments",
      "type": "FILE"
    }
  }
}

Set a value

Setting a file value is a two-step operation: upload, then attach.

Step 1 — Upload the file

Use the uploadFile mutation. This is a GraphQL multipart upload: the file variable is the file part of the request, not an inline string. See Upload Files for the full multipart request format. uploadFile returns a File; keep its uid.

mutation UploadFile($file: Upload!) {
  uploadFile(input: { file: $file, companyId: "company_123", projectId: "project_123" }) {
    uid
    name
    size
    type
    status
  }
}

UploadFileInput

ParameterTypeRequiredDescription
fileUpload!YesThe file to upload (multipart file part).
companyIdString!YesOrganization ID or slug where the file is stored.
projectIdStringNoWorkspace ID or slug. Omit for an organization-level file.
{
  "data": {
    "uploadFile": {
      "uid": "clm4n8qwx000108l0a1b2c3d4",
      "name": "spec.pdf",
      "size": 184320,
      "type": "application/pdf",
      "status": "CONFIRMED"
    }
  }
}

Step 2 — Attach the file to the record

Use createRecordCustomFieldFile with the record ID, the file field ID, and the uid from the upload. It returns Boolean (true on success) — it does not return the created object, so do not add a sub-selection.

mutation AttachFile {
  createRecordCustomFieldFile(
    input: { todoId: "todo_123", customFieldId: "field_123", fileUid: "clm4n8qwx000108l0a1b2c3d4" }
  )
}

CreateRecordCustomFieldFileInput

ParameterTypeRequiredDescription
todoIdString!YesID of the record.
customFieldIdString!YesID of the file custom field.
fileUidString!Yesuid of the uploaded file from uploadFile.
{ "data": { "createRecordCustomFieldFile": true } }

Attaching an already-attached file is a no-op and still returns true. To attach several files, call createRecordCustomFieldFile once per file uid — there is no bulk-attach mutation.

Read a value

Read attached files from the field’s files list. Query the record’s customFields and select files on the FILE field — there is no separate junction type.

query RecordFiles {
  recordQueries {
    todos(filter: { companyIds: ["company_123"], todoIds: ["todo_123"] }) {
      items {
        id
        title
        customFields(filter: { todoId: "todo_123" }) {
          id
          name
          type
          files {
            uid
            name
            size
            type
            extension
            createdAt
          }
        }
      }
    }
  }
}
{
  "data": {
    "recordQueries": {
      "todos": {
        "items": [
          {
            "id": "todo_123",
            "title": "Project deliverables",
            "customFields": [
              {
                "id": "clm4n8qwx000008l0g4oxdqn7",
                "name": "Attachments",
                "type": "FILE",
                "files": [
                  {
                    "uid": "clm4n8qwx000108l0a1b2c3d4",
                    "name": "spec.pdf",
                    "size": 184320,
                    "type": "application/pdf",
                    "extension": "pdf",
                    "createdAt": "2026-05-29T10:14:00.000Z"
                  }
                ]
              }
            ]
          }
        ]
      }
    }
  }
}

File

The files list returns File objects.

FieldTypeDescription
idID!Database ID.
uidString!Stable file identifier; use this as fileUid when attaching or detaching.
nameString!Original filename.
sizeFloat!File size in bytes.
typeString!MIME type.
extensionString!File extension.
sharedBoolean!Whether the file is shared.
statusFileStatusCONFIRMED or PENDING.
createdAtDateTime!Upload timestamp.

Remove a file

Detach a file with deleteRecordCustomFieldFile, identifying it by the same fileUid. It also returns Boolean.

mutation DetachFile {
  deleteRecordCustomFieldFile(
    input: { todoId: "todo_123", customFieldId: "field_123", fileUid: "clm4n8qwx000108l0a1b2c3d4" }
  )
}

DeleteRecordCustomFieldFileInput has the same shape as the attach input: todoId, customFieldId, and fileUid (all String!).

{ "data": { "deleteRecordCustomFieldFile": true } }

Notes

  • Per-file size limits are plan-dependent. The cap is enforced at upload time against your plan’s maximum upload size; exceeding it raises PLAN_LIMIT_REACHED. Hitting your total storage allowance raises the same code.
  • Batch uploads (uploadFiles) are capped at 1 GB total per request; upload files larger than that individually.
  • Files are stored on Blue’s managed object storage (Backblaze B2 as the primary store) and served through short-lived signed URLs. The storage backend is an implementation detail — work with uid and the File fields, not storage paths.

Errors

CodeWhen
CUSTOM_FIELD_NOT_FOUNDThe customFieldId does not exist.
FILE_NOT_FOUNDThe fileUid does not match an uploaded file, or (on detach) the file is not attached to the field.
TODO_NOT_FOUNDThe todoId does not exist.
PLAN_LIMIT_REACHEDUpload exceeds the plan’s per-file size or total storage limit.
FORBIDDENThe caller lacks permission to modify the record.

Permissions

Creating or updating a file custom field requires the OWNER or ADMIN workspace role. Attaching and detaching files requires an ADMIN, OWNER, MEMBER, or CLIENT role on the record’s workspace. Reading files follows standard record-view permissions.