Sales EngineHow it worksFeaturesSolutionsGuidesPricingFAQ Book a tailored demo Ask on WhatsApp
Help Your assistant

Sync your catalogue from your POS

How to let your billing, inventory or ERP system push its catalogue and price sheet into Kooday automatically — and why every push still waits for you to approve it.

Updated 9 September 2026 7 min read

If your prices live somewhere else — a billing system, an inventory tool, an ERP — then keeping Kooday in step by hand is a job nobody remembers to do. The version your assistant quotes drifts away from the version on your counter, and you find out when a customer is told the wrong number.

A catalogue push closes that gap. Your system sends Kooday its catalogue and price sheet whenever it changes, and Kooday works out what is new, what moved, and what is exactly the same as yesterday.

⚠️ A push never changes what your assistant quotes on its own. Everything that arrives lands on the same review screen a website import lands on, and waits for you. There is no setting that turns this off. A wrong price is a wrong price in front of a customer, so a person sees it first — always.

Where to find it: What you sellImport, then open the run at the top of the list. It is the review screen you already know from importing from your website.

What this is good for, and what it is not

Use a catalogue push when your prices are maintained somewhere else and change often — seasonal rates, a supplier price list, stock going in and out.

Do not reach for it if you set your prices in Kooday and nowhere else. Editing What you sell directly is faster and it is the fast path on purpose: a direct edit is live immediately, because you made it and you are looking at it.

Setting it up

The push arrives through an inbound hook — a private address of your own on Kooday, which your system (or your Make/n8n scenario) calls whenever your catalogue changes. Each hook has its own secret, and every call is signed with it.

  1. IntegrationsInbound hooksNew hook.
  2. Leave What should it do? on Push catalogue and prices.
  3. Address name is the last part of the URL — pos-sync is a good one. The full address is shown underneath as you type.
  4. Show it as is optional, and worth filling in: it is what the review screen calls this sync, so a POS push is easy to tell apart from a website import.
  5. Create hook, then copy the secret.

⚠️ The secret is shown once. Kooday keeps it in a form it can check a signature against and cannot show you again. Paste it into your system before closing the box — if you lose it, use Rotate secret on the hook to get a new one, and remember that rotating stops the old one working immediately.

Paste the address and the secret into your system or your Make/n8n scenario. The first push will land as a run full of new rows, because Kooday has not seen any of it before — that is expected, and approving it once is what teaches Kooday which item is which.

Your automation has to sign each call: an HMAC-SHA256 of the timestamp, a full stop, then the raw body, sent as X-Kooday-Signature alongside X-Kooday-Timestamp. The full request contract is in connecting your own systems.

Pausing and removing

Pause stops a hook answering without losing it or its secret — the right control while you are changing something at your end. Remove deletes it: the address stops answering immediately and the same name can be reused later.

What your system sends

One call carries a batch of items. Each item can set:

FieldRequiredWhat it is
skuyesyour own stock code
external_idnoyour system’s own id for the item — defaults to your sku
gtinnobarcode, if you have one
kindyesproduct, service or package
nameyeswhat a customer would call it
descriptionno
statusnoactive, hidden or discontinued
attributesnoyour own labelled fields — aisle, vendor, anything
pricesnoevery price this item has, not only the main one
{
  "external_id": "sync-2026-09-09-01",
  "items": [
    {
      "sku": "CW-001",
      "external_id": "POS-4821",
      "kind": "service",
      "name": "Ceramic Wash",
      "status": "active",
      "prices": [
        {"amount": "1500.00", "currency": "INR",
         "variant_label": "Small", "is_default": true},
        {"amount": "2500.00", "currency": "INR",
         "variant_label": "Large", "is_default": false}
      ]
    }
  ]
}

Two fields worth getting right the first time

external_id is how Kooday recognises the item next time. It is the one value that should never change once you have approved an item. Change it and the next push looks like a brand new product sitting beside the one you already sell. If your system has no stable id of its own, leave it out — your sku is used instead, which is fine as long as that is stable.

prices is the whole ladder, not an addition to it. If an item sells in three sizes, send all three every time. Sending only the one that changed does not update that one — it replaces the ladder with a single rung, and the other two stop existing. Approving that is how a size quietly disappears from what your assistant can offer.

Reading the review screen

Every pushed item lands as one of four kinds of row.

What it meansWhat approving it does
Newnothing in your catalogue matchescreates the item
Changedit matches something you sell, and something about it movedupdates that item
Unchangedit matches, and nothing movednothing — no action needed
Possible duplicateit looks like something you sell, but we are not sure whichnothing. Open the item you think it matches and edit it by hand

The diff under a changed row shows what will actually be written — the name, the status, every price rung, and any of your own labelled fields the push carries. A rung the push dropped is shown struck through, because approving the row removes it.

⚠️ A currency change is highlighted, and it is the one to slow down on. INR 1500 becoming USD 1500 has the same digits and about eighty-five times the money. Kooday will never reject it for you — you might genuinely be repricing — but it will not let it slide past as an ordinary edit either.

Everything on this screen can be ticked in bulk. A push is your own system stating its own facts, not something read off a web page and guessed at, so it does not carry the one-at-a-time restriction a website import puts on rows that were interpreted rather than published.

The second sync is the quiet one

Once you have approved an item, Kooday remembers which of your items it is. From then on a push that changed nothing arrives as unchanged, and a nightly sync of a catalogue that did not move gives you a run with nothing to do.

If every row still comes back as changed every night, the usual cause is an external_id that is not stable — your system is minting a new one each export.

Limits

  • 500 items per call. A larger catalogue is sent across several calls. Over the limit, nothing in that call is staged — you get an error, not half a catalogue.
  • The whole call is accepted or none of it is. One malformed item rejects the batch, so a retry can never duplicate the half that got through.
  • One item per sku, and one per external_id, in a single call. Two lines for the same thing is a duplicate in your export, and Kooday says so rather than guessing which one you meant.

Troubleshooting

Every row says changed, every single night

Your external_id is moving between exports. Check whether your system is sending a row id, an export line number, or a timestamp rather than the item’s own permanent id. Once it is stable, approve one more run — after that, the unchanged rows go quiet.

I approved a push and a size disappeared

The push sent a prices list without that rung. The ladder is replaced, not added to. Send every rung the item has on every push, and re-add the missing one in What you sell.

An item I hid came back

Only if the push said so. A push that does not mention status leaves it alone, so something in your export is sending active for that item. If your system has no notion of hidden, leave status out of the payload entirely.

My system got batch_too_large

More than 500 items in one call. Page your export. Nothing from that call was staged, so there is nothing to clean up before you retry in smaller batches.

I get a 404 from the address

The same answer is given for an address that does not exist and a hook that has been paused — deliberately, so nobody can use the address to discover whether an account exists. Check the hook is switched on in Integrations.

I get a 401, and I am sure the secret is right

The signature covers the timestamp and the body, joined by a full stop — signing the body alone produces a valid-looking signature that will always be rejected. A call whose timestamp is more than five minutes old is refused too, even with a perfect signature, so check the clock on the machine sending it.

Still stuck? We answer support mail the same working day.

Email support