agentsclimarketplace

Create purchase order

Skill skuio/sku-skills/dist/claude/purchase-orders/create-purchase-order

Open-source e-commerce agent skills for the SKU.io API — authored once, generated for Claude, OpenAI, and Gemini.

Install
npx -y skills add skuio/sku-skills --skill create-purchase-order

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 27 days oldThe repository was created 27 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Create a purchase order in SKU.io — a replenishment order placed with a supplier — from either a plain description of what to buy or a source document (a supplier email, an order-form spreadsheet, a demand-plan export) that supplies the SKUs and quantities. Resolves the supplier, destination warehouse, unit costs, currency, and (optionally) payment term, builds the line items, and creates the PO as a draft or an open order. Covers standard and consignment replenishment. Resolve products with find-product first so every line references a real product.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

13.5 KB, ~3.4k tokens by cl100k_base, as published. Nobody here has run it

Create a Purchase Order

Use this skill to create a purchase order — a replenishment order placed with a supplier, with line items — in SKU.io. It maps to POST /api/purchase-orders (scope purchase-orders:write, permission purchase_orders.create), plus a few read lookups to resolve the ids the PO needs.

Step 0 — Connect first

Every call below authenticates as a SKU.io Personal Access Token against one specific tenant, so two things have to be true before Step 1: $SKU_TENANT and $SKU_PAT are set, and that token actually carries purchase-orders:write, suppliers:read, warehouses:read, products:read, settings:read.

If you cannot confirm both, run the connect-to-sku skill first rather than trying a call to see what happens. It mints the token, confirms the tenant is the one the user meant, and reads the scopes back off the token — so a missing scope surfaces now, in one exchange with the user, instead of as a 403 midway through with half the work already committed. If that skill is not installed alongside this one, its instructions are at https://github.com/skuio/sku-skills/tree/main/skills/platform/connect-to-sku.

Never invent a tenant or a token, and never quietly fall back to a different tenant than the one the user named. Writing to the wrong account is the one mistake here the API cannot undo for you.

Two ways in — both converge on the same inputs

A PO request can arrive as either:

  • A description — "reorder 200 of SKU-A and 50 of SKU-B from Acme into the main warehouse."
  • A source document — a supplier email with a spreadsheet, an order form, a demand-plan export. The source gives the unit mix (SKUs + quantities); the header details (supplier, warehouse, terms) come from the surrounding request.

Either way, your job is to assemble one PO:

supplier  +  destination warehouse  +  currency  +  (payment term)  +  lines[ sku, qty, unit cost ]

Work the steps below in order. Resolve every id against the live API — never invent one.

Step 1 — Get the line items (SKUs + quantities + unit cost)

If the input is a description, read the SKUs and quantities straight from it.

If the input is a source document (spreadsheet / email attachment / order form), extract it with your own tools first — the SKU.io API isn't involved yet. Land a normalized table of sku, quantity, unit_cost?. The extraction cautions in the build-product-catalog skill apply verbatim: find the real header row, pick the right tab, skip section headers / subtotals / the grand-total row, and get the attachment out of the email (decode base64 if needed) before parsing.

Read the columns correctly — this is a PO, not a catalog import. On an order form the quantity column is what you want (unlike a catalog import, where you'd ignore it). Map:

  • the item code → resolve to a product (Step 5). A supplier's own item code may be the supplier_sku, not your sku; if a code doesn't resolve, try it as a supplier sku or ask.
  • quantity ordered → line quantity.
  • unit cost / price if present → line amount. If the source has no cost, fill it in Step 5.

Step 2 — Resolve the supplier

GET /api/v2/suppliers?search=<name> → take the id. Disambiguate to a single match; if several plausible suppliers come back, show the candidates (name + company) and ask — don't pick the first. The supplier record also carries default_warehouse_id and default_payment_term_id, which you'll use in Steps 3 and 4.

If the supplier doesn't exist yet, stop and say so — creating suppliers is out of scope here.

Step 3 — Resolve the destination warehouse

GET /api/v2/warehouses returns each warehouse's id, name, code, is_default.

  • Consignment order → pick the account's consignment warehouse (consignment stock is supplier-owned and received into a dedicated warehouse). Match it by name/code. If exactly one consignment warehouse exists (as is typical), use it; if none is clearly the consignment one, or more than one matches, ask rather than guess.
  • Standard order → use the named warehouse, else the supplier's default_warehouse_id, else the is_default warehouse.
  • Dropship order (ships straight to the customer) → omit destination_warehouse_id entirely.

Step 4 — Resolve the payment term (e.g. the consignment term)

payment_term_id is optional, but when the request names a term ("use the consignment payment term") resolve it to an id with GET /api/payment-terms (scope settings:read). Each term in the response carries an is_consignment boolean (and consignment_settlement_frequency), so you resolve the consignment term by that flag — not by guessing at its name. Work in this order:

  1. Consignment order → pick the term whose is_consignment is true. If exactly one exists (typical), use its id; if several do, disambiguate by name or ask.
  2. Named term → match the requested term against each name (case-insensitive). One match → use its id; ambiguous → ask.
  3. Supplier default → otherwise fall back to the supplier's default_payment_term_id (Step 2).
  4. Omit it → create the PO without a term (telling the user it still needs setting) only if they're fine deferring it.

The list is paginated and has no search param, so page through it (?page=) and match client-side. Never guess a payment_term_id — a wrong id silently applies the wrong terms.

Step 5 — Resolve each product and its unit cost

For each line, resolve the product to a real reference with the find-product skill and prefer passing product_id (a line may use product_id or sku; product_id is unambiguous).

Then make sure each line has a unit amount (cost). In priority order:

  1. the unit cost from the source, if it had one;
  2. else GET /api/products/{product}/last-purchase-price — the last price actually paid on a non-draft PO (returns data: null if there's no history);
  3. else the product's supplier price, or ask the user.

Don't invent a cost and don't send amount: 0 to paper over a missing one — a zero-cost line distorts the PO value and, for consignment, the later settlement.

Step 6 — Currency, status, approval

  • Currency — pass currency_code (e.g. "USD", "AUD") matching the account/supplier base currency, or a known currency_id. Resolve or verify it with GET /api/currencies (scope settings:read), which lists the account's enabled currencies with their code and is_default.
  • Status — omit order_status to create an open PO. Set order_status: "draft" (the only accepted value) to leave it as a draft for review. Default to a draft unless the user wants it open.
  • Approvalapproval_status defaults to "pending"; set "approved" only if the user has the authority and asks to approve on creation.

Step 7 — Confirm, then create

Creating a PO commits an order. Before you post, show the user the plan — supplier, destination warehouse, payment term, currency, status, line count, a 2–3 line sample, and the total — and get a go-ahead. Then:

curl -sS -X POST "https://$SKU_TENANT.sku.io/api/purchase-orders" \
  -H "Authorization: Bearer $SKU_PAT" \
  -H "Content-Type: application/json" -H "Accept: application/json" \
  -d '{
    "purchase_order_date": "2026-07-10",
    "supplier_id": 42,
    "currency_code": "USD",
    "destination_warehouse_id": 7,
    "payment_term_id": 5,
    "order_status": "draft",
    "estimated_delivery_date": "2026-08-01",
    "supplier_notes": "Consignment reorder — per Gaelle Wizenburg unit mix.",
    "purchase_order_lines": [
      { "product_id": 1567, "description": "Blue Widget", "quantity": 200, "amount": 4.20 },
      { "product_id": 1568, "description": "Red Widget",  "quantity": 50,  "amount": 4.35 }
    ]
  }'

See examples/request.json for the same body as a file.

Handle the response

  • 200{ "data": { "id", "po_number", "order_status", "total" }, "message" }. Capture the id and po_number; report them, and output the direct link to the PO so the user can click straight into it:

    https://{tenant}.sku.io/v2/orders/purchase-orders/{id}
    

    Always end by giving the user this URL along with the PO number and order status — a PO buried in a table search or filter is easy to miss.

  • 422 → validation failed. The errors map names the fields; line errors use dot-notation, e.g. purchase_order_lines.0.quantity. Common causes: missing purchase_order_date, neither supplier_id nor supplier_name, neither currency_id nor currency_code, or a missing destination_warehouse_id on a non-dropship PO. Fix the named fields and resubmit — never blind-retry. See shared/errors.md.

  • 403 → the token lacks a required scope (purchase-orders:write to create; suppliers:read / warehouses:read / products:read / settings:read for the lookups) or the user lacks purchase_orders.create.

Optionally: submit it to the supplier

Creating a PO does not send it. To send it, POST /api/purchase-orders/submit with { "ids": [<po_id>] }. Submitting emails the supplier — a real, outward-facing action. Only submit when the user explicitly asks, and confirm first; default to leaving the new PO unsent for review. (Approval is separate: create with approval_status: "approved", or approve later.)

Guardrails

  • Confirm before creating, and again before submitting. A PO is a commitment; submitting it contacts the supplier. Don't do either silently.
  • Don't invent ids or costs. supplier_id, destination_warehouse_id, payment_term_id, product_id, and each line amount must be resolved or supplied — blank/ask beats wrong.
  • Consignment specifics. Route to the consignment (supplier-owned) warehouse and apply the consignment payment term; get both right, because consignment settlement later prices sold stock off this PO's costs.
  • Not idempotent. A retry after an ambiguous timeout can create a duplicate PO. On timeout, search recent POs (GET /api/purchase-orders?search=<supplier or po number>) before re-posting.
  • Hold incomplete lines. If a SKU won't resolve or has no defensible cost, set it aside, create the rest, and list what you held for the user to resolve.

API operations

MethodPathWhat it does
GET/api/v2/suppliersFind a supplier by name/email to get its id (and its default warehouse and payment term).
GET/api/v2/warehousesList warehouses (id, name, code, is_default) to resolve the destination warehouse.
GET/api/payment-termsList payment terms to resolve payment_term_id. Each term carries an is_consignment boolean (and consignment_settlement_frequency), so the consignment term is identified by that flag rather than by name-guessing. Paginated; there is no search param — page through and match on the returned fields (id, name, is_consignment, is_default, net_days).
GET/api/currenciesList the account's enabled currencies (id, code, conversion rate, is_default) to resolve or verify currency_code / currency_id.
GET/api/products/{product}/last-purchase-priceMost recent unit cost paid for a product on a non-draft PO — fills a line's amount when the source has no cost.
POST/api/purchase-ordersCreate a purchase order with header fields and an array of line items.
POST/api/purchase-orders/submitSubmit (send to the supplier) one or more created POs. Sends the PO out — confirm first.

Authentication

Every request authenticates with a SKU.io Personal Access Token sent as a Bearer token:

Authorization: Bearer <YOUR_SKU_PAT>
  • Base URL: https://{tenant}.sku.io — replace {tenant} with your account subdomain. The subdomain may itself contain a dot (beta and staging accounts often do), so take everything before .sku.io in the URL you sign in at, not just the first label.
  • Required scopes: purchase-orders:write, suppliers:read, warehouses:read, products:read, settings:read

Mint a token under Settings → Developer → Personal Access Tokens in the SKU.io web app. See shared/authentication.md for the full flow.


Improve this skill

Did this skill fall short—an unclear step, a wrong endpoint, or something it couldn't finish? Don't just work around it: capture what was off and open a pull request so the next agent does better.

  • Repo: https://github.com/skuio/sku-skills
  • Edit the canonical skill under skills/<domain>/<name>/ (not this generated file), then run npm run build and open a PR. External contributors: fork the repo and PR from the fork.
  • The full agent workflow is in AGENTS.md.

Your agent can do this end to end. The library gets better every time someone sends a fix.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.