Cannot create a ticket with attachments in node.js

I have a Node API that receives a formData payload and forwards it to the Freshdesk API.

My code looks like this:

const formData = new FormData();
    Object.keys(payload).forEach((key) => {
      if (!payload[key]) return;
      if (key === 'attachments') {
        for (let i = 0; i < payload[key].length; i++) {
          const file = payload[key][i];
          const stream = Readable.from(file.buffer);
          formData.append('attachments[]', stream, {
            filename: file.originalname,
            contentType: file.mimetype,
      } else {
        formData.append(key, JSON.stringify(payload[key]));
    const res = await axios({
      url: `https://${process.env.FRESHDESK_BASE_URI}${uri}`,
      data: formData,
      auth: {
        username: process.env.FRESHDESK_API_KEY,
        password: null,

The response of the freshdesk api is an odd http response :

HTTP/1.1 0 Unknown Reason-Phrase
Status: 0
X-FD-Queue-Time: 2.003
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Date: Fri, 30 Apr 2021 08:56:00 GMT
Connection: close
X-Powered-By: Phusion Passenger 6.0.4

And the ticket is not created

Any help?


Strange error. Could you possibly log the error statement in the Node API and share it?
The error which you shared seems like a cURL response. And looking through stack overflow, status code 0 means nothing in HTTP and depends on the client.



This is the node output:

  status: 200,
  statusText: 'OK',
  headers: {
    date: 'Fri, 30 Apr 2021 10:16:03 GMT',
    'transfer-encoding': 'chunked',
    connection: 'close',
    'x-fw-ratelimiting-managed': 'true',
    'x-ratelimit-total': '160',
    'x-ratelimit-remaining': '158',
    'x-ratelimit-used-currentrequest': '1',
    'x-envoy-upstream-service-time': '11',
    'x-trace-id': '00-52ac62facbae5a2e7c7c070b978628fd-512477f454775b5a-01',
    server: 'fwe',
    'x-request-id': '6a9dd5e0-be9c-9d2b-ab46-278f884645ee'
  config: {
    url: 'https://[MY DOMAIN]',
    method: 'post',
    data: FormData {
      _overheadLength: 896,
      _valueLength: 83,
      _valuesToMeasure: [Array],
      writable: false,
      readable: true,
      dataSize: 0,
      maxDataSize: 2097152,
      pauseStreams: true,
      _released: true,
      _streams: [],
      _currentStream: null,
      _insideLoop: false,
      _pendingNext: false,
      _boundary: '--------------------------629128260097330139373980',
      _events: [Object: null prototype],
      _eventsCount: 1
    headers: {
      Accept: 'application/json, text/plain, */*',
      'Content-Type': 'application/x-www-form-urlencoded',
      'User-Agent': 'axios/0.21.1'
    auth: { username: '[MY API KEY]', password: null },
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    adapter: [Function: httpAdapter],
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    validateStatus: [Function: validateStatus]
  request: <ref *1> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function (anonymous)],
      aborted: [Function (anonymous)],
      connect: [Function (anonymous)],
      error: [Function (anonymous)],
      socket: [Function (anonymous)],
      timeout: [Function (anonymous)],
      prefinish: [Function: requestOnPrefinish]
    _eventsCount: 7,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: false,
    _last: true,
    chunkedEncoding: true,
    shouldKeepAlive: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: true,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    _contentLength: null,
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    _closed: false,
    socket: TLSSocket {
      _tlsOptions: [Object],
      _secureEstablished: true,
      _securePending: false,
      _newSessionPending: false,
      _controlReleased: true,
      secureConnecting: false,
      _SNICallback: null,
      servername: '[MY DOMAIN]',
      alpnProtocol: false,
      authorized: true,
      authorizationError: null,
      encrypted: true,
      _events: [Object: null prototype],
      _eventsCount: 10,
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: '[MY DOMAIN]',
      _readableState: [ReadableState],
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: undefined,
      _server: null,
      ssl: [TLSWrap],
      _requestCert: true,
      _rejectUnauthorized: true,
      parser: null,
      _httpMessage: [Circular *1],
      [Symbol(res)]: [TLSWrap],
      [Symbol(verified)]: true,
      [Symbol(pendingSession)]: null,
      [Symbol(async_id_symbol)]: 3980,
      [Symbol(kHandle)]: [TLSWrap],
      [Symbol(kSetNoDelay)]: false,
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(connect-options)]: [Object],
      [Symbol(RequestTimeout)]: undefined
    _header: 'POST /api/v2/tickets HTTP/1.1\r\n' +
      'Accept: application/json, text/plain, */*\r\n' +
      'Content-Type: application/x-www-form-urlencoded\r\n' +
      'User-Agent: axios/0.21.1\r\n' +
      'Host: [MY DOMAIN]\r\n' +
      'Authorization: Basic [AUTHORIZATION]\r\n' +
      'Connection: close\r\n' +
      'Transfer-Encoding: chunked\r\n' +
    _keepAliveTimeout: 0,
    _onPendingData: {},
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 443,
      protocol: 'https:',
      options: [Object],
      requests: {},
      sockets: [Object],
      freeSockets: {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 1,
      maxCachedSessions: 100,
      _sessionCache: [Object],
      [Symbol(kCapture)]: false
    socketPath: undefined,
    method: 'POST',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    path: '/api/v2/tickets',
    _ended: true,
    res: IncomingMessage {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      socket: [TLSSocket],
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      rawHeaders: [Array],
      rawTrailers: [],
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 200,
      statusMessage: 'OK',
      client: [TLSSocket],
      _consuming: true,
      _dumped: false,
      req: [Circular *1],
      responseUrl: 'https://[MY API KEY]:@[MY DOMAIN]',
      redirects: [],
      [Symbol(kCapture)]: false,
      [Symbol(kHeaders)]: [Object],
      [Symbol(kHeadersCount)]: 22,
      [Symbol(kTrailers)]: null,
      [Symbol(kTrailersCount)]: 0,
      [Symbol(RequestTimeout)]: undefined
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: false,
    host: '[MY DOMAIN]',
    protocol: 'https:',
    _redirectable: Writable {
      _writableState: [WritableState],
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      _options: [Object],
      _ended: true,
      _ending: true,
      _redirectCount: 0,
      _redirects: [],
      _requestBodyLength: 281926,
      _requestBodyBuffers: [],
      _onNativeResponse: [Function (anonymous)],
      _currentRequest: [Circular *1],
      _currentUrl: 'https://[MY API KEY]:@[MY DOMAIN]',
      [Symbol(kCapture)]: false
    [Symbol(kCapture)]: false,
    [Symbol(kNeedDrain)]: true,
    [Symbol(corked)]: 0,
    [Symbol(kOutHeaders)]: [Object: null prototype] {
      accept: [Array],
      'content-type': [Array],
      'user-agent': [Array],
      host: [Array],
      authorization: [Array]
  data: 'HTTP/1.1 0 Unknown Reason-Phrase\r\n' +
    'Status: 0\r\n' +
    'X-FD-Queue-Time: 2.375\r\n' +
    'X-XSS-Protection: 1; mode=block\r\n' +
    'X-Content-Type-Options: nosniff\r\n' +
    'Date: Fri, 30 Apr 2021 10:16:03 GMT\r\n' +
    'Connection: close\r\n' +
    'X-Powered-By: Phusion Passenger 6.0.4\r\n' +
1 Like

Since there’s a X-Powered-By: Phusion Passenger 6.0.4 in the response, I suspect it is an actual response from the freshdesk API

Ok we figured it out, if it helps anyone:

We were missing boundary headers


  'content-type': 'multipart/form-data; boundary=--------------------------350060439213950768398864'

doesn’t work:

  'content-type': 'multipart/form-data;'

in order to generate the boundary header, we added this:

 const res = await axios({
      url: `https://${process.env.FRESHDESK_BASE_URI}${uri}`,
      data: formData,
-      auth: {
+      headers: formData.getHeaders(),
+      auth: {
        username: process.env.FRESHDESK_API_KEY,
        password: null,