Skip to content

Automatic file upload

Use this route when your system can make the daily Excel file and upload it by itself. Your system sends one file each working day. It signs the upload so that MarsX knows the file came from you.

If people on your team upload the file by hand, use Upload by staff instead. Both routes use the same file and the same rules. MarsX shows a connection that sends files as file-fed, never as real time.

  1. Your system exports one Excel file each working day, in the MarsX template. It holds one row for each lead event.
  2. Your system uploads the file with one signed PUT request.
  3. MarsX stores the file and answers 201. It checks every row and emails you the result.

Use the Excel template, version 1.2. The template columns page lists every column. The lead fields page gives the format of each one.

  • Put one row for each lead event. Use the dropdown arrow wherever a cell has one. In a file, MarsX accepts either the dropdown label or its code.
  • Include only the leads that changed since your previous file. A lead that is missing from a file means no change. It never means deletion.
  • A new or update row carries the lead’s full current state. A blank cell means that field is now empty.
  • A withdraw or delete row carries only sections A and H. Leave every customer, interest, location, source, consent and AI column blank.
  • source_revision goes up with every change to a lead. Never reuse a number for different content. For a change, always send a higher revision number and the time of the change. On a new lead you may leave both blank, and MarsX uses revision 1 and the enquiry time.
  • A new lead needs partner_lead_id, record_action, created_at, customer_name, a phone or an email, source_channel, contact_consent (Yes) and consent_time. A lead without the model or the city is accepted and marked incomplete.
  • Write every date and time with its offset from UTC. In the file, use +07:00.
  • Add privacy rows for every withdrawal and deletion request you received since the previous file, including leads you no longer hold.

Rules for the File info sheet:

Item System name What to enter
Template version template_version Leave as it is. A file on another version is rejected
Partner code partner_code The short code MarsX gave your source, in capital letters
File number file_sequence Six digits. Goes up by one with every file, for example 000123
Date and time of export exported_at When you made the file, for example 2026-10-01T14:30:00+07:00
Number of lead rows row_count The number of filled rows on the Leads sheet. A wrong count rejects the whole file as incomplete
<partner_code>_<YYYY-MM-DD>_<file_sequence>.xlsx

For example, EXAMPLE_2026-10-01_000123.xlsx. The file_sequence goes up by one with every file. MarsX flags a gap.

  • row_count matches the number of rows.
  • The file has no macros, no formulas, no merged cells and no external links. MarsX rejects a file that has any of them.
  • The file is within the limits in the table below.

Your system signs the file bytes the same way it signs an event. Sign the request explains the signing and gives worked examples to prove your code.

Item Value
Address PUT https://api.marsx.media/v1/sources/{connection}/files/{file_name}
Sandbox https://api-sandbox.marsx.media
Body The file bytes
Content type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
webhook-id The file name without .xlsx
webhook-timestamp The time of this attempt, in whole seconds since 1 January 1970 (UTC)
webhook-signature The signature over <webhook-id>.<webhook-timestamp>.<file bytes>
Success 201, after MarsX has stored the file safely

A repeat of the same file is answered 201 with result duplicate. Nothing changes.

SFTP is not offered. Never send a file by email or WhatsApp.

upload-file.mjs
import { createHmac } from "node:crypto";
import { readFileSync } from "node:fs";
import { basename } from "node:path";
const host = "https://api-sandbox.marsx.media";
const connection = process.env.MARSX_CONNECTION; // for example conn_exampleexample22
const secret = process.env.MARSX_SIGNING_SECRET; // starts with whsec_
const path = process.argv[2]; // for example EXAMPLE_2026-10-01_000123.xlsx
const fileName = basename(path);
const body = readFileSync(path);
const id = fileName.replace(/\.xlsx$/, "");
const timestamp = String(Math.floor(Date.now() / 1000));
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const signature =
"v1," + createHmac("sha256", key).update(`${id}.${timestamp}.`).update(body).digest("base64");
const response = await fetch(`${host}/v1/sources/${connection}/files/${fileName}`, {
method: "PUT",
headers: {
"content-type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"webhook-id": id,
"webhook-timestamp": timestamp,
"webhook-signature": signature,
},
body,
});
console.log(response.status, await response.text());
Limit Value
Transport cap of this route, answered 413 before authentication 10 MiB (10,485,760 bytes)
File size on disk, after authentication 10 MiB
Size when expanded 100 MiB
Rows 10,000
Characters in a cell 500

A file over a limit is rejected whole. Other paths have a 1 MiB transport cap. See Limits.

Status Meaning What you do
201 MarsX stored the file safely Done. Wait for the emailed result
400 The request is not valid Fix it. Do not retry the same request
401 The signature is missing or wrong, the clock is off by more than 300 seconds, or the URL or environment is wrong Check the key, the clock, the URL and the environment
413 The file is over the limit of this route Make the file smaller. Do not retry
429 Too many requests. Retry-After says when to try again Wait at least that long, then upload again
502, 503, 504, a timeout or a dropped connection A temporary fault Wait, then upload again with the same webhook-id

See Errors for the full list.

Rule Detail
Schedule One file each working day by 09:00 WIB. Also a file within 24 hours of any privacy request, even if it holds only privacy rows.
File sequence Goes up by one with every file. MarsX flags a gap.
Row count A mismatch rejects the whole file as truncated. A wrong template_version also rejects it.
Content Only the leads that changed since the previous file.
Invalid row Set aside with a reason. Valid rows in the same file still load.
Not accepted Macros, formulas, merged cells and links. MarsX rejects the whole file.
Each connection Has its own upload and its own file sequence. MarsX processes the files of each connection separately.
Before activation Send a made-up sample file.

MarsX finds a missing or partial file from the file sequence and the row count. Then:

  1. MarsX tells you.
  2. You send that file again, unchanged, with its privacy rows, within 24 hours of the notice.
  3. A later file does not replace the missing one. It carries only the privacy requests since the file before it.
  4. Until the gap closes, the connection stays restricted and is shown as having a file gap.
  5. Sending the same file twice changes nothing.

MarsX emails the result of each file to your technical contact. The result is one of three: accepted, rejected with the reason, or a gap in the sequence.

When MarsX asks for it, send a full list of the leads on your connection that are currently withdrawn, restricted or erased, including leads you no longer hold. Send it as a template file with only withdraw and delete rows, named <partner_code>_<YYYY-MM-DD>_privacy-list.xlsx, by the same upload.

Every file also carries withdraw and delete rows for each privacy request since the previous file. See Send a withdrawal or deletion for what MarsX does with them.

  • Never send a lead file by email or WhatsApp

    MarsX does not accept it. Use the signed upload or the staff upload account.

  • Never edit or retype a file after export

    Upload the file exactly as your system produced it. Do not re-key a lead.

  • Never send identity or money documents

    No KTP or family-card numbers, income, financing documents or full chat transcripts. No AI guesses about affordability or location.

  • Never send status or sales outcomes

    No status, assignment, follow-up, test drives, sales or SPK numbers. Dealers record these in MarsX Dashboard.

  • Never reuse a revision number for different content

    The number must rise with every change. Reuse sets the event aside as a conflict.

Run the upload checks with made-up files.