Shipping rates and parcels
What a shipping method charges, the profiles that split an order into parcels, and the drivers a live carrier plugs into.
Shipping is the group about zones, methods and whether a method is offered at all. This one is about the money: the rate rules behind a method, the cost expression that can replace them, the profiles that decide how many parcels an order becomes, and the contract a carrier's own rate card arrives through. See Shipping.
Read a shipping method's rate rules
/api/v1/kahunacart/shipping/methods/{id}/rates
Every rate type on one method — base, per item, per kilogram, percentage, per shipping class — plus the minimum and maximum clamps, the cost expression, the three-valued shipping-class policy and the carrier the method is bound to. Amounts come back as decimal strings in the store currency; the cost expression is in minor units. Also returns the closed token list a cost expression may use. On a store that sells in more than one currency it also returns currency_amounts, priced_in and missing_in for the method.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Shipping method id |
Set a shipping method's rate rules
/api/v1/kahunacart/shipping/methods/{id}/rates
Replace the rate rules on one method. Only the fields you send are written, so a client that has never heard of percent_rate cannot clear one. Rates are tabulated in a fixed order and then clamped, minimum first then maximum. A cost_expression replaces the tabulated figure and is refused with the offending token named if it does not parse. Saving also clears the carrier rate cache.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Shipping method id |
| per_item optional | string or null | Decimal amount per unit in the package |
| per_kg optional | string or null | Decimal amount per kilogram of the package |
| percent_rate optional | string or null | A percentage of the package goods, as a plain decimal |
| min_total_cost optional | string or null | The floor, applied after tabulation |
| max_total_cost optional | string or null | The ceiling, applied after the floor |
| cost_expression optional | string or null | A formula in minor units over the closed token list, e.g. 1000 + ( 200 * [qty] ) + [fee percent="10" min_fee="400"] |
| class_policy optional | string |
allow carries everything; disallow refuses the named classes; require offers the method only for them
One of:
allow, disallow, require |
| class_policy_classes optional | array of string | The shipping classes the policy names |
| class_costs_mode optional | string |
Whether the per-class figures are added to the tabulated rate (add, the default and what they have always meant) or replace it for a parcel holding a line of that class (replace). The two clamps apply either way.
One of:
add, replace |
| carrier_code optional | string or null | The carrier driver that prices this method. KahunaCart ships none |
| carrier_service_code optional | string or null | Which of that carrier's services |
| currency_amounts optional | object | The method's rates in the other currencies, as on a method in a zone: currency code to {cost, free_minimum, per_item, per_kg, min_total_cost, max_total_cost, per_class, weight_rules, cost_expression}. Written with the base fields in one transaction, so a refusal changes nothing. |
Check a shipping cost expression
/api/v1/kahunacart/shipping/expressions/validate
Parse a cost expression without saving it and answer whether it is valid, with the offending token named when it is not. Expressions are evaluated by KahunaCart's own parser over a closed grammar — decimal literals, + - * / ( ), the tokens [qty], [cost], [weight], [item_count] and the one function form [fee percent="N" min_fee="N" max_fee="N"]. Nothing else parses, and no PHP function name ever will.
Parameters
| Name | Type | Description |
|---|---|---|
| expression required | string |
List shipping profiles
/api/v1/kahunacart/shipping/profiles
The profiles a store packs by, in assembly order, with how many products point at each. Two are built in: default ships and digital does not. An order is packed into one parcel per profile its lines mention.
Create a shipping profile
/api/v1/kahunacart/shipping/profiles
Add a profile for a part of the catalogue that travels separately. The code is normalised to lowercase letters, digits, dashes and underscores, and must be unique. ships: false makes a profile that produces a parcel with nothing to post, which is what the built-in digital profile is.
Parameters
| Name | Type | Description |
|---|---|---|
| code required | string | |
| name optional | string | |
| position optional | integer | The order parcels are assembled in |
| ships optional | boolean | Whether a parcel of this profile goes in the post at all |
Update a shipping profile
/api/v1/kahunacart/shipping/profiles/{id}
Change a profile's code, name, position or whether it ships. Renaming a profile does not repoint the products that use it — they point at the id.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Shipping profile id |
| code optional | string | |
| name optional | string | |
| position optional | integer | |
| ships optional | boolean |
Delete a shipping profile
/api/v1/kahunacart/shipping/profiles/{id}
Refused while any product still points at the profile, and refused outright for the built-in default and digital profiles, which are what an unassigned product falls back to.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Shipping profile id |
List an order's parcels
/api/v1/kahunacart/orders/{id}/packages
The packages one order was priced as: which profile assembled each, which method priced it, what it cost, what it weighed, which lines went in it and any carrier metadata for the method that actually won. The order's shipping total is the sum of these. Needs kahunacart.orders.view rather than the shipping settings permission.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Order id |
List carrier rate drivers
/api/v1/kahunacart/shipping/carriers
The live carrier rate drivers installed in this store, the shipping methods bound to them and whether each binding resolves. KahunaCart ships no carrier — the drivers are the KahunaCart Shipping add-on — so an empty list is the correct answer on a store without it. Also returns the timeout, the cache lifetime and the nominated fallback method. Each driver carries a services list — the services it says it has, with a service_code, a label, an optional carrier_code for a driver that fronts several carriers, and the countries it runs in. Listing services is optional, so a driver that cannot enumerate them answers an empty list rather than leaving the key out. Each bound method carries service_known: true when its carrier_service_code is in that driver's list, false when it is not, and null when there is no list to check against. False is a warning and never a refusal — the method is still offered and still priced.
Rebuild the carrier rate cache
/api/v1/kahunacart/shipping/carriers/cache/clear
Drop every stored carrier quote so the next checkout asks the carriers again. Quotes expire on their own after shipping.carrier_rates.cache_ttl seconds and saving a shipping method clears them too; this is the button for a merchant whose courier changed a rate card this morning.
A method's price is tabulated top to bottom and then clamped, and the order is the contract: the base rate, then the per-item rate multiplied by the quantity in the parcel, then the per-kilogram rate, then the percentage of the parcel's item price, then the per-class figure once per distinct shipping class the parcel holds, then the minimum and last the maximum. Both clamps are inclusive at the boundary. Every intermediate is minor units, every rate that is not a whole number of minor units goes through exact rational arithmetic, and the result is rounded once at the end. min_total_cost_minor is the clamp and min_total is the free-shipping threshold. The two names are far apart on purpose.
A cost_expression on a method replaces the whole table above and leaves both clamps in force. Everything in an expression is minor units, so Woo's 10 + ( 2 * [qty] ) is written 1000 + ( 200 * [qty] ) here, and a negative result is floored at zero. The token list is closed: decimal literals, + - * /, brackets, [qty], [item_count], [cost], [weight] and [fee percent="N" min_fee="N" max_fee="N"]. There are no function names, no variables you can invent and no path from a character a merchant types to a PHP callable. Anything outside the list is refused where it is typed, with the offending token named, by POST /kahunacart/shipping/expressions/validate as the merchant writes it and again by the save. Division by zero evaluates to zero and is noted in the adjustment's audit trail rather than taking a checkout down. shipping.expressions.enabled: false refuses to save an expression and ignores any already stored.
PUT /kahunacart/shipping/methods/{id}/rates also carries the three-valued class policy. allow is the default and consults nothing. disallow takes the method off when the parcel holds a line of a named class, require takes it off unless the parcel holds one. A method removed by policy comes back present, disabled and naming the class, as class_disallowed or class_required. A policy naming no classes refuses nothing. The route writes only the fields it is handed, so a client that has never heard of percent_rate cannot clear one.
A shipping profile says which consignment a product belongs to, and an order is packed into one package per profile its lines mention. default and digital ship with the plugin, a product with no profile of its own falls back to digital when it is a download and default otherwise, and an existing store therefore packs every order into one parcel and is charged what it always was. Packages come out in profile position order, the order's shipping total is the sum of its packages, and a package whose profile does not ship gets no method and no cost and is still written down. There is no weight or dimension packing. Deleting the default or digital profile is refused, and so is deleting a profile a product still points at.
KahunaCart ships no carrier. GET /kahunacart/shipping/carriers lists the drivers an add-on has registered, and a stock install answers with none. A carrier never blocks a checkout: an excluded service, a decline, a timeout, a driver that failed and a store with shipping.carrier_rates.enabled: false all come back the same way, as the method present, disabled and carrying a reason code of carrier_excluded, calculator_declined or carrier_unavailable, with the nominated fallback method carrying the order. A quote is cached on a hash of the destination, the parcel contents, its weight, its declared value and the currency, for shipping.carrier_rates.cache_ttl seconds. Saving a method's rates clears the set, and POST /kahunacart/shipping/carriers/cache/clear is the button for the merchant whose account manager changed a rate card this morning.