How to Sync Marketman Purchase Orders to NetSuite Records

Build a scheduled Spojit workflow that pulls recent Marketman buyer orders by sent date, uses an AI agent to map line items into a NetSuite record body, creates the matching record in NetSuite, and posts a reconciliation note to Slack.

What This Integration Does

Restaurant and hospitality teams place purchase orders in Marketman, but finance lives in NetSuite. Re-keying those orders by hand is slow and error-prone: quantities get transposed, units of measure drift, and vendor names never quite match the NetSuite chart. This workflow closes that gap automatically. On a schedule, Spojit reads every Marketman order sent in a recent window, asks an AI agent to translate each order into a clean NetSuite record payload, writes those records to NetSuite, and then drops a short reconciliation summary into a Slack channel so your team can see what synced without opening either system.

The run model is time-based. A Schedule trigger fires on a cron expression (for example, every weekday morning) and emits {{ scheduledAt }}. From there the workflow computes a lookback window, calls Marketman with the get-orders-by-sent-date tool, loops over the returned orders, and creates one NetSuite record per order with the create-record tool. The workflow leaves new records in NetSuite plus a Slack note as its only side effects, so re-runs over an overlapping window can create duplicates unless you de-duplicate on a stable key (covered in Common Pitfalls). Treat each scheduled run as "sync everything sent since the last window," and keep the window aligned to your cron interval.

Prerequisites

  • A Marketman connection (API key) added under Connections. If you manage more than one buyer, note the buyer GUID you want to sync; otherwise the connection's default buyer is used.
  • A NetSuite connection authorized to create the record type you are targeting (for example a purchase order or a custom record). Confirm the exact recordType name and required fields in your NetSuite account first.
  • A Slack connection with permission to post to the channel where reconciliation notes should land.
  • A short mapping reference: which Marketman fields (vendor, item, quantity, unit cost) correspond to which NetSuite fields. The AI agent uses this to build the record body, so the clearer your mapping, the cleaner the output.
  • Access to the Workflow Designer with permission to create scheduled workflows.

Step 1: Add a Schedule trigger

Create a new workflow and add a Trigger node, then set its type to Schedule. Enter a 5-field Unix cron expression and an IANA timezone. For a weekday 7am sync in Sydney, use:

0 7 * * 1-5
Australia/Sydney

A single Schedule trigger can hold multiple cron entries if you need more than one fire time. The trigger output is {{ scheduledAt }}, an ISO timestamp you will use to anchor the lookback window in the next step. Keep the cron cadence and the window width in sync so you read each order exactly once.

Step 2: Compute the sent-date window

Marketman's get-orders-by-sent-date tool expects a start and end date in yyyy/mm/dd hh:mm:ss format. Add a Connector node in Direct mode using the date utility connector to build those two strings, or use a single Transform node to derive them from {{ scheduledAt }}. For a daily run, set the end to "now" and the start to 24 hours earlier (add a little overlap if your cron can slip).

Produce two values, for example {{ window.from }} and {{ window.to }}, each formatted like:

2026/06/20 07:00:00
2026/06/21 07:00:00

Marketman expects these in UTC, so convert from your local timezone when you format. The date connector's format and subtract tools, or timezone to shift to UTC, handle this cleanly.

Step 3: Pull recent Marketman orders

Add a Connector node in Direct mode for the Marketman connector and pick the get-orders-by-sent-date tool. Map the inputs:

  • dateTimeFromUTC to {{ window.from }}
  • dateTimeToUTC to {{ window.to }}
  • buyerGuid (optional) to your buyer GUID, or leave blank to use the connection default

Direct mode is the right choice here because this is a single, predictable read with no AI cost. Bind the result to a variable such as {{ marketmanOrders }}. The tool returns the orders sent in your window; the list of orders becomes the input you loop over next. If you regularly process large windows, narrow the date range rather than widening it, so each run stays light.

Step 4: Loop over each order

Add a Loop node set to ForEach and point it at the orders array from {{ marketmanOrders }} (drill into the field that holds the order list). Inside the loop body, each iteration exposes the current order as a variable such as {{ order }}, including its vendor, line items, quantities, and costs.

Looping one order at a time keeps the mapping prompt small and makes each NetSuite write independently retryable. If you expect high volume per run, you can place the per-order work inside a Parallel node fan-out instead, but start with a sequential ForEach loop until you have confirmed the mapping is correct.

Step 5: Map line items with an AI agent

Inside the loop, add a Connector node in Agent mode. Agent mode is the right tool here because translating a Marketman order into a NetSuite record body is judgment work: matching vendors, normalizing units, and summing line items. Write a prompt that hands the agent the current order and your mapping rules:

You are mapping a Marketman purchase order into a NetSuite record body.
Marketman order: {{ order }}

Map vendor, each line item (item ref, quantity, unit cost), and order
total into NetSuite fields. Normalize units of measure. Output ONLY the
JSON body for a NetSuite create-record call. Do not invent fields.

Attach a Response Schema to force structured JSON so downstream steps get a predictable shape. Define fields that mirror your NetSuite record (for example entity, tranDate, memo, and an items array of { item, quantity, rate }). Bind the result to {{ mappedRecord }}. Because the schema constrains the output, you avoid the free-text drift that breaks the NetSuite write in the next step.

Step 6: Create the NetSuite record

Still inside the loop, add a Connector node in Direct mode for the NetSuite connector and choose the create-record tool. Map the inputs:

  • recordType to the exact record type you are targeting, for example purchaseOrder
  • body to the AI-mapped payload {{ mappedRecord }}

An example body shaped by the Response Schema looks like:

{
  "entity": "1042",
  "tranDate": "2026-06-20",
  "memo": "Marketman order {{ order.OrderNumber }}",
  "items": [
    { "item": "388", "quantity": 12, "rate": 4.50 },
    { "item": "401", "quantity": 6, "rate": 9.00 }
  ]
}

Bind the result to {{ netsuiteResult }} so you can capture the new internal ID for the reconciliation note. Keep this in Direct mode: the write is deterministic and you do not want AI deciding which record to create. If a record fails validation, the NetSuite error surfaces in the run's execution logs for that iteration.

Step 7: Post a reconciliation note to Slack

After the loop, add a Connector node in Direct mode for the Slack connector and select the send-message tool. Set the channel and compose a summary that confirms what synced. For example:

Marketman to NetSuite sync complete for {{ scheduledAt }}.
Orders read: {{ marketmanOrders.count }}
NetSuite records created: {{ createdCount }}
Window: {{ window.from }} to {{ window.to }} (UTC)

If you want per-order detail, accumulate a list inside the loop (using a Transform or the array connector's join tool) and include it in the message body. To reach a teammate by address rather than by channel, use Slack's lookup-user-by-email tool first, then send the message to the resolved user. This single note gives finance a daily checkpoint without logging into either system.

Tips

  • Keep the lookback window slightly wider than the cron interval (for example a 25-hour window on a daily run) so a delayed run never skips orders, then de-duplicate on a stable order key.
  • Always format the Marketman dates as yyyy/mm/dd hh:mm:ss in UTC. A locale-formatted date string will return the wrong orders or none at all.
  • Use a tight Response Schema on the Agent mode node. Constraining the JSON shape is what makes the NetSuite create-record body reliable and keeps AI credit usage low.
  • Ask Miraxa, the intelligent layer across your automation, to scaffold the canvas: "Build a scheduled workflow that reads Marketman orders by sent date, maps each with an agent, creates a NetSuite record, and posts a Slack summary." Then fine-tune each node in the properties panel.

Common Pitfalls

  • Duplicate records on re-run. Overlapping windows can create the same NetSuite record twice. Before create-record, look up whether the order already exists (a NetSuite run-suiteql query keyed on your Marketman order number works well) and skip with a Condition node if it does.
  • Wrong record type or missing required fields. NetSuite rejects a body that omits mandatory fields for the recordType. Confirm the required fields in NetSuite first and encode them in the Response Schema so the agent always supplies them.
  • Timezone drift. Your cron timezone and Marketman's UTC date window are separate concerns. Convert {{ scheduledAt }} to UTC when you build dateTimeFromUTC and dateTimeToUTC, or you will read a window offset by your local UTC difference.
  • Slack channel access. If the reconciliation note never appears, confirm the Slack connection can post to that channel and that the channel ID (not just the name) is correct.

Testing

Before turning the schedule on, validate end to end on a small scope. Temporarily replace the Schedule trigger with a Manual trigger so you can run on demand from the Run button, and set the date window to a single day you know contains a couple of Marketman orders. Run once and inspect the execution logs: confirm get-orders-by-sent-date returned the expected orders, that the Agent mode node produced a clean record body matching your Response Schema, and that create-record returned a new internal ID. Verify the records in NetSuite and check the Slack note. Once a manual run is correct, switch back to the Schedule trigger and enable the workflow.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.