Netsuite to Portatour - form-data file transfer

I am trying to integrate Netsuite with Portatour. The idea is that I want to transfer Saved Search (JSON) data into Portatour which only accepts CSV data but with an ‘importconfig’ file which is a JSON structured mapping file that will map the CSV headers with the field names in Portatour.

I have set up a connection to Portatour in Postman but the structure is very odd. It uses HTTP Body form-data transfer of files, one each for the CSV data and one for the importconfig file. I have attached a screenshot of how this looks for clarity.

I have set up the flow and a postMap script on import to try and convert the NetSuite saved search data to a CSV blob but it doesnt seem to work right.
I would like to know if this is possible with Celigo.

Use "Transfer files into destination application" as the import type with your HTTP connection to Portatour.

  • HTTP method: POST
  • Relative URI: /a/api/ImportCustomers?mode=UpdateOrInsert

The request body needs two inline parts for the multipart form:

[
  {
    "name": "file",
    "value": "{{csvData}}",
    "type": "inline",
    "mime-headers": {
      "Content-Disposition": "form-data",
      "content-type": "text/csv",
      "filename": "customers.csv"
    }
  },
  {
    "name": "importConfig",
    "value": "{{importConfigJson}}",
    "type": "inline",
    "mime-headers": {
      "Content-Disposition": "form-data",
      "content-type": "application/json",
      "filename": "customers-template.importcfg"
    }
  }
]

A few things worth noting:

  • type: "inline" means you're providing content directly as a string — works perfectly here since both the CSV and importConfig are text-based
  • type: "attachment" with "value": "{{blob}}" is for when you have a blobKey from a prior blob export, which isn't needed here
  • The docs mention only one file reference (blob type) per import, but since both parts are inline this shouldn't be an issue
  • Don't include the boundary — Celigo adds it automatically
  • Make sure to set the header Content-Type: multipart/form-data

Reference: Celigo docs on multipart/form-data

For generating the CSV from the NetSuite saved search data, I'm using a preSavePage hook on the export. One thing to keep in mind — preSavePage runs per page, not across all pages. So either set the export page size high enough to capture all customers in one go, or accept that each page becomes a separate CSV upload (which should be fine since Portatour's UpdateOrInsert mode is idempotent).

function preSavePage(options) {
  const records = options.data;
  if (!records || !records.length) {
    return {
      data: [],
      errors: options.errors,
      abort: false,
      newErrorsAndRetryData: []
    };
  }

  const columns = ['ExternalId', 'CompanyName', 'Street', 'ZipCode', 'City', 'Country'];

  let csv = columns.join(',') + '\n';
  for (const rec of records) {
    const row = columns.map(col => {
      let val = (rec[col] || '').toString().replace(/"/g, '""');
      return `"${val}"`;
    });
    csv += row.join(',') + '\n';
  }

  return {
    data: [{
      csvData: csv,
      importConfigJson: JSON.stringify({ /* your importcfg content */ })
    }],
    errors: options.errors,
    abort: false,
    newErrorsAndRetryData: []
  };
}

If the importConfig is always the same (just a field mapping template), you can hardcode it directly in the request body template instead of passing it dynamically.

Overall flow ends up being:

NetSuite Saved Search Export (JSON)
  → preSavePage hook (convert records to CSV string)
  → HTTP Transfer to Portatour (multipart/form-data, 2 inline parts)
1 Like

Thanks for this response, it is very helpful. However I realise that I am required to have both files transferred from Netsuite as attachments. Using your instructiuons I have managed to do this with the CSV but since I can only put one blobKey in and I can only use {{blob}} once in the handlebars template, how can I reference two files?

Maybe you can insert a json object in the importConfig? And only use the {{blob}} for the CSV:

[
  {
    "name": "file",
    "type": "attachment",
    "value": "{{blob}}",
    "mime-headers": {
      "Content-Disposition": "form-data",
      "content-type": "text/csv",
      "filename": "customers.csv"
    }
  },
  {
    "name": "importConfig",
    "type": "inline",
    "value": "{  ...your actual importcfg JSON content here...  }",
    "mime-headers": {
      "Content-Disposition": "form-data",
      "content-type": "application/json",
      "filename": "customers-template.importcfg"
    }
  }
]
1 Like

This worked perfectly. Thank you

1 Like