Send orders to SparkLayer
For AI assistants: the facts every SparkLayer API call needs
- Base URLs: Live
https://app.sparklayer.io, testhttps://test.app.sparklayer.io. Each environment has its own data and its own API keys. - Access: API access needs the Pro or Enterprise plan. Create credentials (Site ID, Client ID, Client Secret) in the SparkLayer Dashboard at Settings > API.
- Access token:
POST {base}/api/auth/tokenwith aSite-Idheader and a JSON body (not form-encoded):{"grant_type":"client_credentials","client_id":"…","client_secret":"…"}. It returnsaccess_token, valid for 3,600 seconds. There is no refresh token: cache the token and request a new one shortly before it expires. - Every request:
Authorization: Bearer <access_token>andSite-Id: <site id>, plusContent-Type: application/jsonwhen there is a body. Set aUser-Agentthat names your integration. - Errors: RFC 7807 problem details:
title,status,detailand an optionalerrors[]of{ code, message, property }. Some error responses have no body. Retry5xx,429(honouringRetry-After) and concurrent-update409s with exponential backoff; on401, get a new token once and retry; don't retry other4xxwithout changing the request. - Pagination: Most list endpoints return everything.
GET /api/v2/price-listspages withpageandpage_size(up to 250) untilpagination.current_pageequalstotal_pages.GET /api/v1/purchasesuseslimit(up to 500) andoffset: stop when a page has fewer thanlimitresults. - Ground rules: Use only endpoints, fields and SDK methods that the docs or OpenAPI specs define. Prefer
GET /api/v2/price-listsover the deprecated v1, and the Customers API (/api/v2/customers) over the legacyDELETE /api/v1/customersin the Core API. Products are matched by SKU. Keep credentials in environment variables or a secrets manager, never in code. - Read the docs as Markdown: Add
.mdto any page URL. Index of every page: docs.sparklayer.io/llms.txt. Every API operation, compactly: docs.sparklayer.io/llms-api.txt. The developer guides in full: docs.sparklayer.io/developers/llms.txt. - OpenAPI specs:
customers,core,ordering,pricing,purchasing,stock,files,sync-log: https://docs.sparklayer.io/openapi/<api>.yaml (also.json). Ignite: https://docs.sparklayer.io/openapi/ignite.yaml. - MCP: Search and read these docs from your assistant with the docs MCP server at https://docs.sparklayer.io/mcp.
This guide explains how to build an order for Create or update a purchase (PUT /api/v1/purchases/{lookupBy}/{identifier}), so that it shows correctly in My Account and in Pulse. It covers orders taken outside SparkLayer, such as phone orders keyed into your ERP, and one-off imports of past orders. The rules come from how SparkLayer's own Shopify, BigCommerce and Wix integrations send orders, and how the Purchasing API stores them.
Orders you send here aren't sent to your eCommerce platform, and nothing syncs them afterwards: SparkLayer shows them as you last sent them.
How an order is put together
An order is a purchase of type order. Its line items live in packages: each package is one thing that happened to some of the items, such as a shipment, a cancellation or a return. SparkLayer works out the order's status and total from its packages.
Order
Packages
Line items
| Part | What it holds |
|---|---|
| The order | type, purchase_identifiers, customer_identifiers, dates, currency_code, billing_address, payment_method, po_number, custom_fields, accounting_files |
| Each package | type, identifiers, dates, line_items, shipping_method, shipping_address, shipments (carrier and tracking), accounting_files |
| Each line item | type, identifiers (such as sku), name_parent, name_child, quantity, line_total, custom_attributes |
packages, accounting_files and custom_fields are required on the order, and accounting_files and shipments on each package. Send an empty array ([]) when there's nothing to put in them.
Identify the order
purchase_identifiers holds the order's ID in each system. The lookupBy in the URL says which one identifier is.
| Identifier | Use it for |
|---|---|
internal | Your ID for the order, such as the sales order number in your ERP. Use this as lookupBy for orders from your own systems. |
visible | The order number customers see in My Account. Often the same as internal. |
platform | The order's ID on your eCommerce platform, if it has one. |
sparklayer | SparkLayer's ID (a UUID). Leave it out for orders from your own systems: SparkLayer generates one when it creates the order. Only send it for an order placed through SparkLayer, as the SparkLayer cart ID, so the order replaces the cart. |
- The body must contain the ID in the URL. For
PUT /api/v1/purchases/internal/SO-10452, the body needs"purchase_identifiers": { "internal": "SO-10452" }. A missing or different value is rejected with a400. - Lookups are exact and case-sensitive.
so-10452doesn't findSO-10452. - Keep IDs to 64 characters, and the
po_numbertoo. - SparkLayer doesn't stop two orders sharing an ID. If 2 orders have the same
internalID, a lookup by it finds either of them. Give every order its own ID. - Never generate a new
sparklayerID each time you send an order. A lookup by a new ID never finds the existing order, so every request creates another copy.
Orders that are already in SparkLayer
Orders placed through SparkLayer, and orders your eCommerce platform syncs to SparkLayer (such as Shopify orders marked for import), are already there. If you send the same order from your ERP as well, it shows twice. Before importing an order you also have on your platform, check with Get a purchase, with lookupBy=platform and the platform's order ID, and skip it if it's found.
Choose the customer
customer_identifiers says who the order belongs to.
| Identifier | Who |
|---|---|
sparklayer | Required. The SparkLayer ID of the customer account the order belongs to. For company users, this is the main account, not the person who ordered. |
sparklayer_child | The company user who placed the order, if it wasn't the main account. |
sparklayer_impersonator | The sales rep who placed the order for the customer, if one did. |
My Account lists an order for the account in customer_identifiers.sparklayer, and its company users see it too.
To find a customer's SparkLayer ID, look them up with Get a customer in the Customers API, by their ID on your eCommerce platform or by their email address. Use the id in the response:
GET /api/v2/customers/platform/{platform-customer-id}
GET /api/v2/customers/email/{url-encoded-email}The Purchasing API doesn't check that the customer exists. An order for an ID SparkLayer doesn't know is stored, but no customer sees it in My Account and it drops out of sales reps' figures in Pulse. Make sure every customer is in SparkLayer before you send their orders, including customers who no longer buy from you. On Shopify, a customer syncs once they have the b2b tag: see Tag existing customers in Shopify. For a past customer who shouldn't be able to sign in, create them with Create or update a customer and status set to inactive: their orders count in Pulse, and you can make them active later.
Packages
Each package has a type, and the dates that go with it. SparkLayer works out a package's status from its dates, not its type, so always set the date that matches the type. A cancelled package with no cancelled_at, for example, counts as still processing, and its items count towards the total.
Package type | Dates to set | Counts as |
|---|---|---|
processing | None | Processing: ordered, not yet shipped |
shipment | shipped_at | Shipped |
cancelled | cancelled_at | Cancelled. Left out of the total |
return | return_started_at, and return_completed_at once the return is complete | Returned. Taken off the total |
incoming | None | Only for purchases with no placed_at. Orders always have one, so never send incoming for an order |
Give dates in RFC 3339 format, with a time zone offset or Z for UTC: 2024-03-18T14:00:00Z.
Rules for packages
- Every unit ordered is in exactly one
processing,shipmentorcancelledpackage. Add up those packages and you get the quantities ordered. - Returned units stay in their shipment. List them again in a
returnpackage. SparkLayer subtracts returns from what was shipped, so a return package with no shipment package is rejected. - Send one package for each real event: each shipment, each cancellation and each return, with its own date.
- Put the shipping method on one package only. Use the first shipment, or the
processingpackage if nothing has shipped. Setshipping_methodtonullon every other package: each package with a shipping method adds its shipping cost to the total. Shipping on a cancelled or returned package is ignored. - Amounts are for the package's quantity, not the whole order line. If 6 of 10 units ship, the shipment's
line_totalis for 6 units: see Line items and amounts. - Give each package a
sparklayer_package_idthat never changes. It's a UUID you generate, unique within the order. Send the same ID for the same package every time, for example a version 5 UUID made from your order number and shipment number. SparkLayer treats a package with a new ID as a new package.
identifiers.internal and identifiers.visible on a package are your references for it, such as a delivery note number. shipments holds the carrier and tracking details: carrier, carrier_id (the tracking number) and tracking_url.
A part-shipped order
10 units of HB-200 were ordered. 6 have shipped and 4 are still to ship, so the order has a shipment package for 6 and a processing package for 4. The order's status is part_shipped. Amounts are left out to keep the example short.
[
{
"type": "shipment",
"identifiers": { "sparklayer_package_id": "0b7c1f4e-5a2d-5c8e-9f31-6d2a7e4b8c10", "internal": "SO-10452-1" },
"dates": { "shipped_at": "2024-03-18T14:00:00Z" },
"line_items": [{ "identifiers": { "sku": "HB-200" }, "name_parent": "Handlebar grip", "quantity": 6, "line_total": { "…": "…" } }],
"shipping_method": { "name": "Standard delivery", "line_total": { "…": "…" } },
"shipments": [{ "carrier": "DPD", "carrier_id": "15501234567890", "tracking_url": null }],
"accounting_files": []
},
{
"type": "processing",
"identifiers": { "sparklayer_package_id": "6f1d2c3b-8e4a-5b7c-a1d2-3e4f5a6b7c8d" },
"line_items": [{ "identifiers": { "sku": "HB-200" }, "name_parent": "Handlebar grip", "quantity": 4, "line_total": { "…": "…" } }],
"shipping_method": null,
"shipments": [],
"accounting_files": []
}
]When the other 4 ship, send the whole order again with a second shipment package for 4 and no processing package. The status becomes shipped.
Cancellations
- Whole order cancelled before anything shipped: one
cancelledpackage with every unit, dated when it was cancelled. The status iscancelledand the total is zero. - Some units cancelled: take them out of the
processingpackage and put them in acancelledpackage with itscancelled_at. If the rest is still to ship, the status staysprocessing. If the rest has shipped, it'svaried: see Order status.
Returns and refunds
- Whole order returned: keep the shipment packages, and add a
returnpackage with every unit that shipped. When the units returned equal the units shipped, the status isreturned. - Some units returned: keep the shipment packages, and add a
returnpackage with the units that came back. The status ispart_returned. - A refund with no items returned or cancelled, such as a goodwill credit, isn't a package. Record the money with a transaction.
SparkLayer compares the total quantity shipped with the total quantity returned across the whole order, not SKU by SKU.
Order status
SparkLayer calculates the order's status, calculated_fulfilment_status, from the statuses of its packages every time you send the order. It's read-only, so don't send it. There are no rules about which status can follow which: send the packages as they are now, and the status follows.
| The order's packages are | Status |
|---|---|
| All processing | processing |
| All shipped | shipped |
| Shipped and processing | part_shipped |
| All cancelled | cancelled |
| Processing and cancelled | processing |
| Shipped and returned, every shipped unit returned | returned |
| Shipped and returned, some shipped units returned | part_returned |
| Any other mix, such as shipped and cancelled | varied |
No placed_at on the purchase | incoming |
Line items and amounts
Each line item needs identifiers, name_parent, quantity and line_total.
type:productfor a product in your catalogue, orcustomfor anything else, such as a service or a fee.identifiers.sku: the SKU, up to 128 characters. SparkLayer matches order lines to your products by SKU, for example in Pulse's product figures.identifiers.sparklayer_variant_id: the variant's ID on your eCommerce platform, if you know it.name_parentandname_child: the product and variant names. They're kept as sent, so the order still reads correctly if the product is renamed or deleted later.custom_attributes: extra details for the line, askeyandvaluepairs, such as engraving text.
Every amount is a money object: a number as a string and a 3-letter currency, such as { "number": "72.00", "currency": "EUR" }.
line_total field | What to send |
|---|---|
total_net | Required. The line's price before tax, after discounts, for the package's quantity. |
discount_net | Required. The discount already taken off total_net, for the customer's information. Use "0.00" for no discount. |
total_tax | The tax on the line. |
total_gross | total_net plus total_tax. |
total_net_base, discount_net_base, total_tax_base, total_gross_base | The same amounts in your store's base currency: see Currencies. |
- Every
line_totalneeds a value on an order. Only quotes can have anullline total. total_net,discount_netand tax-line amounts must be in the order'scurrency_code. A different currency is rejected.- Split amounts across packages by quantity. For a line of 10 at
120.00net, a shipment of 6 hastotal_net72.00and a processing package of 4 has48.00. - Leave out
tax_rate. It's deprecated: send the tax amount intotal_tax, or astax_lines.
The shipping method has the same line_total fields, plus a name and an optional sku.
How SparkLayer adds up the total
calculated_total is the order's gross total. SparkLayer calculates it, so don't send it. It adds up:
- The net and tax of every line in
processingandshipmentpackages. - Minus the net and tax of every line in
returnpackages. - The net and tax of the shipping method on every package that isn't cancelled or returned.
- Lines in
cancelledpackages count for nothing.
If you send tax_lines on the order itself, SparkLayer uses their sum for the tax instead of the line-by-line tax, minus the tax on cancelled and returned lines. Leave them out unless your system gives you order-level tax lines (for example several tax rates) that you want customers to see.
Currencies
Send the order's currency in currency_code. It's required.
Pulse reports in your store's base currency. Set it once, before your first order, with Update the store currency, or send it with every order in currency_code_base.
- Orders in the base currency: you don't need to send any
_baseamounts. SparkLayer copies them from the order's amounts. - Orders in another currency: send every
_baseamount yourself, converted at the rate on the order's date:total_net_base,discount_net_base,total_tax_baseandtotal_gross_baseon every line item and shipping method, andamount_baseon every tax line.
If an order in another currency is missing a _base amount, SparkLayer converts its total at today's exchange rate instead, and Pulse leaves the order out of its revenue figures. calculated_total_usd always uses today's rate.
SparkLayer recalculates totals every time you send an order. If you set the store currency after sending orders, send them again to fill in their base totals.
Dates
| Field | What to send |
|---|---|
dates.placed_at | Required for orders: when the order was placed. Pulse and order statistics use this date, so imported orders appear on their real dates. |
dates.payment_due_at | When payment is due, if you have it. |
shipping_requested_date | The delivery date the customer asked for, if any. |
revision_date | When the order last changed in your system. SparkLayer rejects an update with an older revision_date than the one it holds (409), so a delayed or repeated request can't overwrite newer data. |
dates.created_at and dates.updated_at are set by SparkLayer and can't be sent: they're the dates the order was stored. Sending a read-only field, such as created_at or calculated_fulfilment_status, is rejected with a 400.
Payments and the account balance
The account balance
Each customer has an account balance: what they owe you, shown in the Financials section of My Account when you use credit limits and pay on account.
- Creating an order adds its total to the customer's balance, dated
placed_at. Updating it later adjusts the balance by the change in total. - A transaction with
apply_to_balance: truetakes its amount off the balance, datedtransaction_date.
Without an opening balance, the balance is every order and payment added together, however old. When a customer has an opening balance with a balance date, only orders and payments dated on or after that date count.
Before you import past orders for a customer who pays on account, give them an opening balance dated after the newest order you'll import, so old orders don't change what they owe. On any platform, set it with Adjust a customer's account balance and type set to opening_balance:
{
"type": "opening_balance",
"amount": 1250.00,
"date": "2026-11-01T00:00:00Z"
}Payment status
SparkLayer calculates payment_status (unpaid, part_paid or paid) and total_paid from transactions, for orders placed on or after 6 March 2026. For earlier orders, it doesn't: send payment_status and total_paid yourself if you want them shown, and SparkLayer keeps them as sent. Pulse only counts orders placed on or after 6 March 2026 as amounts owed.
Transactions
Record payments and refunds with Create or update a purchase transaction, after the order exists. Send:
| Field | What to send |
|---|---|
platform_id | The transaction's ID in your payment or accounting system. Sending the same platform_id again for the order updates that transaction. |
type | What it is, such as payment or refund. SparkLayer stores it as text. |
amount | The amount as a string: positive for a payment, negative for a refund. |
currency_code | The 3-letter currency code. |
transaction_date | When the payment was made. |
apply_to_balance | true to count it towards the payment status and the account balance, false to store it for information only. |
full_transaction | The transaction as your system holds it, as a JSON string. Remove card numbers and other sensitive data first. |
See Purchase transactions for what apply_to_balance does.
Send an order again
Sending an order with the same lookupBy and identifier updates it rather than creating a copy, so an import can be run as often as you like.
- Send the whole order every time. The request replaces everything stored: a package you leave out is removed.
- A few values are kept if you leave them out: the
sparklayerID,payment_status,total_paidanddates.created_at. - Keep every ID the same: the identifier you look up by and each
sparklayer_package_id. - Send
revision_date, so an older version can't replace a newer one.
Delete a purchase removes an order from My Account and Pulse. Sending it again with the same identifier brings it back.
Errors
| Status | Cause | Fix |
|---|---|---|
400 | A required field is missing or invalid. The error names the field. | Correct the field and send the order again. |
400 | The ID in the URL isn't in purchase_identifiers, or doesn't match. | Put the same ID in the body. |
400 | "Line item total cannot be null for orders". | Give every line item a line_total. |
400 | "Invalid package status". | Use processing, shipment, cancelled or return packages, and set dates.placed_at. |
400 | A read-only field was sent, such as created_at. | Leave out read-only and calculated fields. |
400 | The order's total is over 99,999,999.9999. | Check the amounts. |
400 or 500 | A return package with no shipment package. | Keep returned units in their shipment package too. |
403 | The order was found, but the sparklayer ID in the body is different. | Leave sparklayer out, or send the stored one. |
409 | "A newer revision already exists": revision_date is older than the stored one. | Nothing to do: SparkLayer already has a newer version. |
409 | "Duplicate SparkLayer ID": the sparklayer ID belongs to another order. | Leave sparklayer out for orders from your own systems. |
500 | An ID or po_number over 64 characters. | Shorten it. |
See Errors for the error format and when to retry.
A complete order
Order SO-10452 was placed on 14 March 2024, in euros, by a customer of a store whose base currency is pounds (1 EUR = 0.85 GBP on the order date). The customer ordered 10 handlebar grips and 4 seats, with 15.00 shipping, all with 20% VAT:
- On 18 March, 6 grips and the 4 seats shipped.
- On 25 March, the other 4 grips shipped.
- On 2 April, the customer started a return of 1 seat, which was completed on 9 April.
That's 2 shipment packages and a return package. 14 units shipped and 1 came back, so the status is part_returned. The total is 402.00 ordered, less the 60.00 seat returned: 342.00 EUR, or 290.70 GBP.
PUT /api/v1/purchases/internal/SO-10452{
"type": "order",
"purchase_identifiers": {
"internal": "SO-10452",
"visible": "SO-10452"
},
"customer_identifiers": {
"sparklayer": "4217ba4f-7618-4e3b-b1af-9055639b18d1"
},
"dates": {
"placed_at": "2024-03-14T09:30:00Z",
"payment_due_at": "2024-04-13T00:00:00Z"
},
"revision_date": "2024-04-09T16:00:00Z",
"currency_code": "EUR",
"currency_code_base": "GBP",
"payment_method": "paymentByInvoice",
"po_number": "PO-7781",
"customer_reference": null,
"billing_address": {
"company": "Harbour Cycles Ltd",
"address_line1": "12 Quay Street",
"city": "Cork",
"postal_code": "T12 X2Y3",
"country_code": "IE"
},
"custom_fields": [],
"accounting_files": [],
"packages": [
{
"type": "shipment",
"identifiers": {
"sparklayer_package_id": "0b7c1f4e-5a2d-5c8e-9f31-6d2a7e4b8c10",
"internal": "SO-10452-1",
"visible": "SO-10452-1"
},
"dates": { "shipped_at": "2024-03-18T14:00:00Z" },
"shipping_address": {
"company": "Harbour Cycles Ltd",
"address_line1": "12 Quay Street",
"city": "Cork",
"postal_code": "T12 X2Y3",
"country_code": "IE"
},
"shipping_method": {
"name": "Standard delivery",
"line_total": {
"total_net": { "number": "15.00", "currency": "EUR" },
"discount_net": { "number": "0.00", "currency": "EUR" },
"total_tax": { "number": "3.00", "currency": "EUR" },
"total_gross": { "number": "18.00", "currency": "EUR" },
"total_net_base": { "number": "12.75", "currency": "GBP" },
"discount_net_base": { "number": "0.00", "currency": "GBP" },
"total_tax_base": { "number": "2.55", "currency": "GBP" },
"total_gross_base": { "number": "15.30", "currency": "GBP" }
}
},
"shipments": [
{ "carrier": "DPD", "carrier_id": "15501234567890", "tracking_url": null }
],
"accounting_files": [],
"line_items": [
{
"type": "product",
"identifiers": { "sku": "HB-200" },
"name_parent": "Handlebar grip",
"name_child": "Black",
"quantity": 6,
"line_total": {
"total_net": { "number": "72.00", "currency": "EUR" },
"discount_net": { "number": "18.00", "currency": "EUR" },
"total_tax": { "number": "14.40", "currency": "EUR" },
"total_gross": { "number": "86.40", "currency": "EUR" },
"total_net_base": { "number": "61.20", "currency": "GBP" },
"discount_net_base": { "number": "15.30", "currency": "GBP" },
"total_tax_base": { "number": "12.24", "currency": "GBP" },
"total_gross_base": { "number": "73.44", "currency": "GBP" }
}
},
{
"type": "product",
"identifiers": { "sku": "SD-110" },
"name_parent": "Comfort seat",
"name_child": null,
"quantity": 4,
"line_total": {
"total_net": { "number": "200.00", "currency": "EUR" },
"discount_net": { "number": "0.00", "currency": "EUR" },
"total_tax": { "number": "40.00", "currency": "EUR" },
"total_gross": { "number": "240.00", "currency": "EUR" },
"total_net_base": { "number": "170.00", "currency": "GBP" },
"discount_net_base": { "number": "0.00", "currency": "GBP" },
"total_tax_base": { "number": "34.00", "currency": "GBP" },
"total_gross_base": { "number": "204.00", "currency": "GBP" }
}
}
]
},
{
"type": "shipment",
"identifiers": {
"sparklayer_package_id": "9a4e8c2d-1b3f-5d6e-8a7b-2c4d6e8f0a1b",
"internal": "SO-10452-2",
"visible": "SO-10452-2"
},
"dates": { "shipped_at": "2024-03-25T11:15:00Z" },
"shipping_address": {
"company": "Harbour Cycles Ltd",
"address_line1": "12 Quay Street",
"city": "Cork",
"postal_code": "T12 X2Y3",
"country_code": "IE"
},
"shipping_method": null,
"shipments": [
{ "carrier": "DPD", "carrier_id": "15501234567999", "tracking_url": null }
],
"accounting_files": [],
"line_items": [
{
"type": "product",
"identifiers": { "sku": "HB-200" },
"name_parent": "Handlebar grip",
"name_child": "Black",
"quantity": 4,
"line_total": {
"total_net": { "number": "48.00", "currency": "EUR" },
"discount_net": { "number": "12.00", "currency": "EUR" },
"total_tax": { "number": "9.60", "currency": "EUR" },
"total_gross": { "number": "57.60", "currency": "EUR" },
"total_net_base": { "number": "40.80", "currency": "GBP" },
"discount_net_base": { "number": "10.20", "currency": "GBP" },
"total_tax_base": { "number": "8.16", "currency": "GBP" },
"total_gross_base": { "number": "48.96", "currency": "GBP" }
}
}
]
},
{
"type": "return",
"identifiers": {
"sparklayer_package_id": "3c5e7a9b-2d4f-5a6c-9e8d-7f1a3b5c7d9e",
"internal": "RMA-2231",
"visible": "RMA-2231"
},
"dates": {
"return_started_at": "2024-04-02T10:00:00Z",
"return_completed_at": "2024-04-09T16:00:00Z"
},
"shipping_address": null,
"shipping_method": null,
"shipments": [],
"accounting_files": [],
"line_items": [
{
"type": "product",
"identifiers": { "sku": "SD-110" },
"name_parent": "Comfort seat",
"name_child": null,
"quantity": 1,
"line_total": {
"total_net": { "number": "50.00", "currency": "EUR" },
"discount_net": { "number": "0.00", "currency": "EUR" },
"total_tax": { "number": "10.00", "currency": "EUR" },
"total_gross": { "number": "60.00", "currency": "EUR" },
"total_net_base": { "number": "42.50", "currency": "GBP" },
"discount_net_base": { "number": "0.00", "currency": "GBP" },
"total_tax_base": { "number": "8.50", "currency": "GBP" },
"total_gross_base": { "number": "51.00", "currency": "GBP" }
}
}
]
}
]
}The response is the stored order, with calculated_fulfilment_status of part_returned, calculated_total of 342.00 EUR and calculated_total_base of 290.70 GBP.
Import order history
To load past orders once, so customers see them in My Account and your team sees them in Pulse:
- Start in the test environment. Create an API key in Test mode, and import a few orders there first.
- Set the store base currency with Update the store currency, if it isn't set.
- Make sure every customer is in SparkLayer, and map each customer in your export to their SparkLayer ID: see Choose the customer.
- Set opening balances for customers who pay on account, dated after the newest order you'll import: see The account balance.
- Leave out orders that are already in SparkLayer: see Orders that are already in SparkLayer.
- Build each order from your order, shipment, return and credit note records, with the rules above. Send them one at a time, looked up by
internalID, and log every failure with its error. - Run it again for the orders that failed, once you've fixed them. Orders that went through are updated, not copied.
- Check the results in My Account, signed in as a customer, and in Pulse. Pulse picks up new orders within about 4 hours.
There's a ready-made prompt for this in Build with AI, and a guide for merchants in Import order history.
Next steps
Last updated