File/Image Uploads

Uploading a file or image via the Knack API is a two step process. The first step will perform an HTTP POST request with a multipart/form-data content type to one of the following URLs, depending on what you are uploading:

  • for files: https://api.knack.com/v1/applications/app_id/assets/file/upload
  • for images: https://api.knack.com/v1/applications/app_id/assets/image/upload

The second step is to then create a new record using the information from the response in the first step.

🚧

Asset uploads should only ever be connected to individual fields.

Knack does not support the same asset file (representing one file upload) to be connected to more than one field. It is okay to have the same file/image for different fields/records but these will be different copies of the same asset file. If you wish to upload the same file and connect it to multiple fields, you must re-upload the file for each field you wish to connect the file to.


Classic Knack Implementation


👍

**Classic **Knack example

1. Uploading the File

The following is a cURL request which uploads a file (replace “file” with “image” when uploading an image file):

curl -X POST "https://api.knack.com/v1/applications/YOUR-APP-ID/assets/file/upload" \
  -H 'content-type: multipart/form-data' \
  -H 'x-knack-rest-api-key: YOUR-API-KEY' \
  -H 'x-knack-application-ID: YOUR-APP-ID'\
  -F "files=@/path/to/your/file.txt"

File uploading can also be done via JavaScript/AJAX (replace “file” with “image” when uploading an image file):

var app_id = Knack.application_id;

    var form = new FormData();
    // change #myfileinput to select your <input type="file" /> element
    var file = $(`#myfileinput`)[0].files[0];
    form.append(`files`, file);

    var url = `https://api.knack.com/v1/applications/${app_id}/assets/file/upload`;
    var headers = {
      'X-Knack-Application-ID': app_id,
      'X-Knack-REST-API-Key': `knack`,
    };

    Knack.showSpinner();

    // Make the AJAX call
    $.ajax({
      url: url,
      type: `POST`,
      headers: headers,
      processData: false,
      contentType: false,
      mimeType: `multipart/form-data`,
      data: form,
    }).done(function(responseData) {
      console.log(responseData);
      Knack.hideSpinner();
    });

A successful POST will return the following JSON response representing details about the uploaded file:

{
    "id": "623b3878eff91f001ecef70a",
    "type": "file",
    "filename": "SOMEFILENAME.txt",
    "public_url": "https://api.knack.com/v1/applications/61606f1f1d50ef001f38678a/download/asset/623b3878eff91f001ecef70a/SOMEFILENAME.txt",
    "thumb_url": "",
    "size": 1365
}

That public_url property can be used to download the file.

2. Creating the Record

This response contains an id property that you will use in the next step. The public_url property will contain a direct link to the file itself, and the thumb_url property will contain a link to a thumbnail if an image was uploaded.

Use the file_id from the JSON response to make a second POST request to create a record referencing your file. Set that file_id to your image or file field.

The following is a cURL example, assuming field_1 is a file or image field:

curl -X POST "https://api.knack.com/v1/objects/object_xx/records" \
  -H "x-knack-rest-api-key: YOUR-API-KEY" \
	-H "x-knack-application-id: YOUR-APP-ID" \
	-H "content-type: application/json" \
	--data '{"field_1": "YOUR-FILE-ID"}'

Once the file is associated to a specific record, when you call API to retrieve records there is a value like this:

{
    ....
    "field_240": "<a class=\"kn-view-asset\" data-field-key=\"field_240\" data-asset-id=\"623b35c9eff91f001ecef6fc\" data-file-name=\"SOMEFILENAME.txt\" href=\"https://api.knack.com/v1/applications/61606f1f1d50ef001f38678a/download/asset/623b35c9eff91f001ecef6fc/SOMEFILENAME.txt\">SOMEFILENAME.txt</a>",
    "field_240_raw": {
        "id": "623b35c9eff91f001ecef6fc",
        "application_id": "61606f1f1d50ef001f38678a",
        "s3": true,
        "type": "file",
        "filename": "SOMEFILENAME.txt",
        "url": "https://api.knack.com/v1/applications/61606f1f1d50ef001f38678a/download/asset/623b35c9eff91f001ecef6fc/SOMEFILENAME.txt",
        "thumb_url": "",
        "size": 454,
        "field_key": "field_240"
    },
    ....
}

The field_xxx_raw.url property can be used to retrieve the file content.

Next-Gen Knack Implementation


👍

Next-Gen Knack Example

Knack.ready().then(async () => {
  console.log('File upload system initialized');
  
  // Your verified app ID
  const APP_ID = '657fbee1ff695e0027091234';
  
  // Main file upload function
  async function uploadFileToKnack(file, isImage = false) {
    const assetType = isImage ? 'image' : 'file';
    const url = `https://api.knack.com/v1/applications/${APP_ID}/assets/${assetType}/upload`;
    
    const headers = {
      'X-Knack-Application-ID': APP_ID,
      'X-Knack-REST-API-Key': 'knack',
    };
    
    const formData = new FormData( );
    formData.append('files', file);
    
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: headers,
        body: formData,
      });
      
      if (!response.ok) {
        throw new Error(`Upload failed: ${response.status} ${response.statusText}`);
      }
      
      const result = await response.json();
      console.log('File uploaded successfully:', result);
      return result;
      
    } catch (error) {
      console.error('Upload error:', error);
      throw error;
    }
  }
  
  // Create record with uploaded file
  async function createRecordWithFile(fileId, fieldKey, objectKey, additionalData = {}) {
    const url = `https://api.knack.com/v1/objects/${objectKey}/records`;
    
    const headers = {
      'X-Knack-Application-ID': APP_ID,
      'X-Knack-REST-API-Key': 'knack', // Use your API key if needed
      'Content-Type': 'application/json',
    };
    
    const recordData = {
      [fieldKey]: fileId,
      ...additionalData
    };
    
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: headers,
        body: JSON.stringify(recordData ),
      });
      
      if (!response.ok) {
        throw new Error(`Record creation failed: ${response.status} ${response.statusText}`);
      }
      
      const result = await response.json();
      console.log('Record created successfully:', result);
      return result;
      
    } catch (error) {
      console.error('Record creation error:', error);
      throw error;
    }
  }
  
  // Complete upload process (upload file + create record)
  async function completeFileUpload(file, fieldKey, objectKey, additionalData = {}) {
    try {
      console.log(`Starting upload for: ${file.name}`);
      
      // Step 1: Upload file
      const uploadResult = await uploadFileToKnack(file);
      
      // Step 2: Create record
      const recordResult = await createRecordWithFile(
        uploadResult.id, 
        fieldKey, 
        objectKey, 
        additionalData
      );
      
      return {
        file: uploadResult,
        record: recordResult
      };
      
    } catch (error) {
      console.error('Complete upload failed:', error);
      throw error;
    }
  }
  
  // Setup file input handler
  function setupFileUpload(inputSelector, fieldKey, objectKey) {
    const fileInput = document.querySelector(inputSelector);
    
    if (!fileInput) {
      console.warn(`File input not found: ${inputSelector}`);
      return;
    }
    
    fileInput.addEventListener('change', async (event) => {
      const file = event.target.files[0];
      if (!file) return;
      
      try {
        const result = await completeFileUpload(file, fieldKey, objectKey);
        
        // Success feedback
        console.log('Upload completed successfully!', result);
        alert(`File "${file.name}" uploaded successfully!`);
        
        // Optional: Clear the input
        fileInput.value = '';
        
      } catch (error) {
        console.error('Upload failed:', error);
        alert(`Upload failed: ${error.message}`);
      }
    });
    
    console.log(`File upload handler setup for: ${inputSelector}`);
  }
  
  // Make functions globally available
  window.KnackFileUpload = {
    uploadFile: uploadFileToKnack,
    createRecord: createRecordWithFile,
    completeUpload: completeFileUpload,
    setupInput: setupFileUpload
  };
  
  // Example usage - uncomment and modify as needed:
  // setupFileUpload('#my-file-input', 'field_1', 'object_1');
  
  console.log('✅ Knack Next-Gen file upload system ready!');
  console.log('Available methods: window.KnackFileUpload');
});

How to Use This Code

  1. For Custom File Inputs
// Add this inside the Knack.ready() block:
setupFileUpload('#my-file-input', 'field_123', 'object_456');

  1. For Manual Uploads
// Use the global functions:
const file = document.querySelector('#my-input').files[0];
window.KnackFileUpload.completeUpload(file, 'field_123', 'object_456')
  .then(result => console.log('Success!', result))
  .catch(error => console.error('Failed!', error));

  1. Integration with Knack Events
// Add inside Knack.ready():
Knack.on('view:render:view_123', ({ viewKey }) => {
  // Setup file upload when a specific view loads
  setupFileUpload(`#${viewKey} input[type="file"]`, 'field_1', 'object_1');
});

What You Need to Customize

  • Replace field and object keys with your actual Knack field/object keys
  • Replace input selectors with your actual HTML element selectors
  • Customize success/error handling as needed