Install
openclaw skills install @zmtucker/drivethru-adidas-clickBrowser-driven adidas Click B2B ordering toolkit — places purchase orders on the adidas Click portal with Playwright, since adidas exposes no public ordering API. Accepts style number, color, size, quantity, a purchase-order number, and a ship-to address, drives the cart/checkout, and (on confirm) submits the order. Use whenever the user needs to place or draft a purchase order on adidas Click.
openclaw skills install @zmtucker/drivethru-adidas-clickadidas Click is a B2B ordering portal with no public API, so this skill places purchase orders by driving the portal with Playwright. Every tool is reached through one CLI entrypoint:
echo '<json-args>' | python3 scripts/adidas.py <action>
The action is the first CLI argument; arguments are a JSON object on
stdin. Each call prints a single JSON object on stdout, or
{"error": {"type": ..., "message": ...}} with a non-zero exit code on failure.
Playwright is imported lazily, so the skill loads without it. The Playwright
package is a declared uv dependency; its Chromium browser binary (a
separate ~150 MB download that pip/uv doesn't fetch, and for which OpenClaw has
no install-time hook) is installed automatically on the first run if missing
— so no manual python -m playwright install chromium is needed. That first run
therefore includes a one-time browser download. If the auto-install fails (e.g.
no network, or missing system libraries), the tool returns a clear
config_error pointing at the manual command.
Build status — full order flow implemented. The skill drives a purchase order end to end: login → add line quantities to the active cart → checkout (Customer PO, delivery location — default / saved / one-time dropship — and shipping method) → Next → Calc. Net Price (waits for the "Done!" tell so the wholesale discount always applies) → Order Now → confirmation number parsed from the redirect URL.
confirm: falseis a full dry run;confirm: trueplaces a real order (no sandbox). The assembled flow has been built and unit-checked selector-by-selector but not yet run against the live site in one pass — watch the first real end-to-end run. Seereferences/order_flow_notes.mdfor the step map and captured selectors.
Reach for create-purchase-order when the request involves placing (or
drafting) a purchase order on adidas Click: given a style number, color,
size, quantity, a PO number, and a ship-to address, put the order into the
portal cart/checkout and — on explicit confirmation — submit it.
Do not use it for other footwear/apparel vendors, and never invent portal
selectors from prose — the flow lives in the captured
references/order_flow_notes.md.
| Action | Risk | stdin JSON (key fields) |
|---|---|---|
create-purchase-order | high — portal write (browser) | {purchase_order: {po_number, lines: [{style, size, quantity}], ship_to?, delivery_location_id?, ship_method?, on_insufficient_stock?, spread_delivery?}, confirm} |
Run python3 scripts/adidas.py with no action to print the action list.
create-purchase-order inputThe order can be passed nested under purchase_order or as top-level fields:
{
"purchase_order": {
"po_number": "PO-12345",
"lines": [
{"style": "JW4306", "size": "M", "quantity": 24},
{"style": "JW4306", "size": "L", "quantity": 12}
],
"ship_to": null,
"delivery_location_id": null,
"ship_method": null,
"spread_delivery": false,
"notes": null
},
"confirm": false
}
Each line is {style, size, quantity}. color is optional — adidas article
numbers encode the color (e.g. JW4306 = the black colorway), so navigating to
the article lands on the right color with no picker.
Delivery location precedence: delivery_location_id (pick a saved location) >
ship_to (add a one-time / dropship location) > default (leave the cart's
preset). A one-time ship_to.state accepts a 2-letter abbreviation or the full
name. ship_method is left at the cart default unless set, and accepts a FedEx
service code (FDGP, FEDE, FED4, …) or its exact label ("FedEx Ground").
po_number is capped at 18 characters and may contain only letters, numbers,
space, and / _ . ? &; any other character (e.g. a hyphen) is auto-replaced with
_ and the substitution is reported in the result's warnings.
By default (new_cart: true) the run creates its own fresh cart (named with
the PO) right after login and makes it active, so on a shared account it never
adds to — or checks out — a teammate's cart. If a cart with the same PO name
already exists (e.g. a leftover from an errored prior run) it is deleted
first so the run never piles onto stale quantities — only carts with that exact
PO name are removed, and any deletion is noted in warnings. Pass
new_cart: false to use the current active cart instead.
Pass screenshot_path to capture the filled page for review.
Before entering quantities, the tool reads each size's availability (scrolling
the horizontal size row as needed to load off-screen sizes). A size is either
backorderable (stock below the requested quantity, incl. 0 with a restock
date) or not available (the "X" cell — will never be restocked, cannot be
ordered at all; it can only be removed or substituted). When a line's full
quantity is not available, behavior depends on on_insufficient_stock:
pause (default) — nothing is ordered (even with confirm: true). The
result comes back status: "needs_confirmation" with an out_of_stock list
({style, size, requested, available}) and a message. The agent must stop
and confirm with the user, then re-run.order — order the short line(s) anyway, accepting delayed delivery
(adidas spreads the backordered portion over future dates).skip — remove the out-of-stock line(s) and order the rest.Agent guidance: if the user's request pre-authorizes a choice, set the flag
and skip the pause — e.g. "go ahead and order anything out of stock" →
on_insufficient_stock: "order"; "remove any out-of-stock items without
asking" → on_insufficient_stock: "skip". Otherwise leave it at pause; on a
needs_confirmation result, message the user with the out_of_stock details
and the three choices, wait for their answer, then resume:
on_insufficient_stock: "order".on_insufficient_stock: "skip".lines accordingly and re-run (optionally with a policy for any still-short
substitutes).spread_delivery is the lower-level knob behind order: on a per-line spread
prompt, false (default) declines (single delivery), true accepts.
The result reports total_quantity (summed pieces, on dry runs too) and, on a
placed order (confirm: true), each line's net line_total and unit_price
(net ÷ quantity) plus the order order_total (net wholesale), read from the
priced review page.
The adidas Click portal login can be supplied three ways (later wins):
Environment variables (preferred for a deployed agent):
ADIDAS_CLICK_USERNAME=...
ADIDAS_CLICK_PASSWORD=...
ADIDAS_CLICK_BASE_URL=https://b2bportal.adidas-group.com # override for region host
Inline in the stdin JSON — pass username, password, and base_url
alongside the tool's own arguments.
CLI flags — --username / --password / --base-url after the action.
Handy for local runs with no env vars and no secrets in the order JSON:
echo '{"purchase_order": {...}, "confirm": false}' \
| python3 scripts/adidas.py create-purchase-order --username "$U" --password "$P"
Command-line arguments are visible to other processes (
ps) and your shell history — for sensitive/automated use, prefer env vars or the stdin JSON.
If no credentials are present via any of these, the tool exits with
{"error": {"type": "config_error", ...}} (exit code 2) — treat that as a
signal to ask the user for the missing fields. Never guess or reuse credentials
across accounts, or paste secrets the user did not provide.
create-purchase-order is the only (side-effecting) action.
"confirm": true to place the order. Without it, it logs in,
fills the cart + checkout, and returns a dry-run preview without
submitting. Confirm with the user before placing.confirm: true for an order the user actually intends to
place.Failures print {"error": {...}} and exit non-zero:
config_error (exit 2) — missing/invalid credentials or an un-captured step.api_error — the portal rejected an action or a page did not match
expectations. Includes surface, operation, retryable.connection_error — network / browser-launch / navigation failure
(retryable: true).validation_error — bad input JSON or a missing required field.usage / unknown_action (exit 2) — bad CLI invocation; the message lists
the valid actions.Surface the human-readable message to the user. Do not retry on
config_error or validation_error.
references/order_flow_notes.md — the
reverse-engineered portal ordering flow (step map, captured selectors, and the
capture checklist for the next step). Start here to continue the build.