Skip to main content
Customize Smart Send with WordPress filters and actions in your own plugin. Hooks let you adjust checkout, delivery choices and booking behavior without editing Smart Send. Use the metadata reference to understand stored values and the PHP API to read pickup points, shipping methods and bookings for a WooCommerce order.

Filters and actions

Choose the stage that matches your customization. Each stage and hook below links to its detailed contract and example. $is_return is false for outbound and true for return wherever the signature includes it. A combined booking can run a callback twice, once for each direction. Events with a Booked_Shipment instead expose $shipment->is_return().
smart_send_booking_completed means Smart Send has created one shipment. It fires before the plugin saves its booking information to the WooCommerce order. Use smart_send_order_fulfilled when you need the final order-side result, including an outbound success with a return failure.
Signatures list the filter value first, followed by context arguments; states the required return type. Actions have no useful return value. Examples are independent customizations, not a file to install in full. Open a hook and select Description for its contract or Example for the PHP customization and usage guidance.

Checkout availability and rates

Filter · (bool $is_available, array $package, Smart_Send\Shipping_Method\Method $method) → boolRuns after the method’s shipping-class and customer-role checks. Return false to hide this Smart Send instance for the package; return the incoming value to preserve its existing restrictions. Returning true does not create a price outside the weight table. Checkout only; no booking or order updates. This example additionally limits the method to Denmark.
Filter · (bool $is_free, array $package, Smart_Send\Shipping_Method\Method $method) → boolRuns after the configured minimum/coupon condition is evaluated, when the cart weight is allowed. true selects Flat fee cost, which is not necessarily zero; false uses normal weight-price calculation. It does not override zone, role, class or weight availability. The example disables the flat-fee branch, preserving ordinary rate calculation.
Action · (Smart_Send\Shipping_Method\Method $method, array $rate) → voidRuns at the end of a Smart Send rate calculation. $rate contains id, label, cost, meta_data and package. It can run even when weight rules produced no rate, so check $method->rates before deriving an extra option. The example adds 10 in the store currency to an existing rate, before WooCommerce tax calculation. It preserves the original service metadata; it does not book a different carrier service.

Pickup points

Filter · (array $params) → arrayRuns before a nearest-point API lookup when a token is configured. Keys: carrier, country, postal_code, street (strings) and city (string or null). Return all keys. The API determines result count; trim results with smart_send_pickup_points_found. Search parameters do not rewrite the customer’s order address. This example removes surrounding postal-code whitespace.
Filter · (Smart_Send\Delivery\Pickup_Point[] $points, array $params) → Smart_Send\Delivery\Pickup_Point[]Runs after a non-empty API result has become typed points, before session caching/display. Return a reindexed list of compatible Pickup_Point objects. The plugin removes objects that do not match the lookup’s carrier/country. It does not call this filter for an empty API result. Reordering influences Select Default only when no compatible explicit customer choice exists; this is not a way to revoke an existing compatible selection.
Filter · (string $label, Smart_Send\Delivery\Pickup_Point $point) → stringRuns when a pickup label is formatted, after Dropdown format. Return plain text, not HTML or pre-escaped entities. Each renderer escapes its output. This affects display, not the point’s identity or saved address. Identity is the exact agent number together with carrier and country.
Filter · (int $seconds) → int|floatDefault: 4 seconds. Runs for both nearest-point and single-point API requests. Return a positive timeout in seconds. Increasing it can lengthen checkout waits; it does not guarantee results. The example allows six seconds.

Shipping method settings

Filter · (array $fields) → arrayWooCommerce calls this when reading the global Smart Send settings field definitions. Return the complete field array. Editing a field’s description/default does not change a previously saved value or implement behavior for a new field. The example adds guidance to the existing validation button.
Filter · (array $fields) → arrayWooCommerce calls this when reading a shipping-zone instance’s fields. Return the complete definitions. The example clarifies the customer-facing title; it changes neither the booked service nor saved settings.

Fulfillment decisions

Filter · (array $carriers, WC_Order $order, bool $is_return) → arrayRuns separately for the outbound and return service lists on the order screen. Shape: each carrier has code, name, services; each service has code, name, addons (currently an empty array). A booking method is carrier_service, for example postnord_agent. Return the filtered carrier list. The resolved selected method is always re-added if missing, even if you return []. This narrows the UI; it is not an authorization boundary. Enforce booking rules in smart_send_delivery_details. The example offers only PostNord for outbound, subject to that selected-method safeguard.
Filter · (Smart_Send\Delivery\Delivery_Details $details, WC_Order $order, bool $is_return) → Smart_Send\Delivery\Delivery_DetailsRuns once per attempted direction after stored configuration, resolved service and submitted overrides are merged, before pickup validation and booking. Return the details object. Use set_shipping_method(), set_pickup_point() or set_parcel_plan(); use clear_pickup_point() for an explicit clear in a partial details object. Throw Smart_Send\Booking\Exceptions\Booking_Exception to reject a business rule before booking. Filter changes affect this booking; they are not automatically persisted. See delivery details and persistence.
Filter · (float $weight, Smart_Send\Delivery\Parcel_Spec $spec, WC_Order $order) → floatRuns per parcel without an explicit weight. Input is the allocated items’ total in kg, possibly zero. Return a finite usable weight including packaging. An explicit Parcel_Spec::set_weight() or order-screen weight bypasses the filter. An allocated deleted product with unknown weight produces a validation error before the filter; provide an explicit positive parcel weight instead. There is no direction argument; the filter can run for outbound and return.

Order data for booking

Filter · (array $shipping_address, int $order_id) → arrayRuns when reading the order’s shipping address, before phone/email fallback and conversion to receiver data. Uses WooCommerce address keys such as first_name, last_name, company, address_1, address_2, postcode, city, country, and optional phone/email. Return the complete address. The second argument is the order ID, not a WC_Order. Both directions use the same reading stage; this filter does not save the address.
Filter · (string|null $phone, WC_Order $order) → string|nullRuns after choosing shipping phone, or billing phone when shipping and billing countries match, then trimming whitespace. Return a phone number or null. No direction flag is supplied. The example removes spaces without guessing a country calling code.
Filter · (array $receiver, WC_Order $order) → arrayRuns after receiver mapping and phone/email fallback. Keys: company, name_line1, name_line2, address_line1, address_line2, postal_code, city, country (strings), phone and email (string or null). Return all keys. This is plugin-level data, not a raw HTTP payload, and it is used in either direction. The example removes accidental whitespace around a non-null email.
Filter · (array[] $items, WC_Order $order) → array[]Runs after reading one row per order line, before allocation to parcels, in either direction. Return the same identities and valid quantities; use smart_send_delivery_details to distribute them. See the item row schema. Net/tax values are discounted line totals, not unit prices. The example trims a customs description without fabricating one for a deleted product.
Filter · (array $totals, WC_Order $order) → arrayRuns after order totals have been derived and negative net/tax values clamped to zero. Return all keys in the totals schema. Later values are used as returned; preserve reconciliation and non-negative amounts. Order-level fees are included in shipment totals, whereas parcel totals contain allocated merchandise. The example logs only the currency and order total, leaving the booking values unchanged.
Filter · (string|null $freetext, WC_Order $order) → string|nullRuns when reading text for the label, in either direction. Input is the customer’s order comment when Include order comment on label is enabled, otherwise null. Return text or null; do not return HTML. This does not control WooCommerce order-history notes. The example omits customer comments from labels.

Booking request and events

Filter · (Smart_Send\Booking\Shipment $shipment, WC_Order $order, bool $is_return) → Smart_Send\Booking\ShipmentRuns after data extraction and parcel construction, immediately before translation into the API request. Return a Shipment. Prefer earlier filters for allocation/weight rules because this is after parcel validation. The example adds an outbound-only internal reference; it does not update the order or persist delivery settings.
Action · (Smart_Send\Booking\Booked_Shipment $booked, Smart_Send\Booking\Shipment $request, WC_Order $order) → voidRuns once per successfully booked direction, after the API response is mapped and before order-side persistence or document copying. Use $booked->is_return() for direction. It does not mean the whole outbound/return run succeeded, and a document’s local copy is not ready yet. Do not throw from the listener: a shipment already exists. Use smart_send_order_fulfilled when you need stored order results.
Action · (Smart_Send\Booking\Exceptions\Booking_Exception $exception, Smart_Send\Booking\Shipment $request, WC_Order $order) → voidRuns when the booking API call throws a handled HTTP client exception, just before it is rethrown as Booking_Exception. Read getMessage(), errors() (field → message list), and response_id() (string or null). This is not an all-failures event: local allocation, pickup or delivery validation can fail before the API call. No direction boolean is provided. The example logs a response ID, without address data or field values.

Order side effects and final result

Filter · (bool $save, Smart_Send\Booking\Booked_Shipment $shipment, WC_Order $order) → boolRuns after successful booking and persistence of submitted delivery overrides, before document-copy attempts and shipment-history persistence. Defaults to Save a copy of the PDF for either direction. Return false to skip local copies. A copy failure adds a warning; the booked shipment remains fulfilled and document URLs fall back to Smart Send. The example keeps documents remote.
Filter · (string $note_html, Smart_Send\Booking\Booked_Shipment $shipment, WC_Order $order) → stringRuns after shipment history is saved, before the native order note is added. Return safe HTML/text, or '' to omit the note. Notes are saved using WC_Order::add_order_note() when the caller requests a note. WooCommerce’s history refreshes after a page reload; the Smart Send result can appear immediately. The example writes a short plain-text note for either direction.
Filter · (bool $push, Smart_Send\Booking\Booked_Shipment $shipment, WC_Order $order) → boolRuns after the order-note step, before forwarding parcel tracking to the optional WooCommerce Shipment Tracking plugin. Default: true outbound, false return. This does not create live tracking updates or install that plugin. Returning false leaves the booked shipment’s own tracking data intact. The example disables forwarding.
Filter · (string|false $status, Smart_Send\Booking\Booked_Shipment $shipment, WC_Order $order) → string|falseRuns after tracking. Return a registered WooCommerce status such as wc-completed, or false to leave the order unchanged. Default: configured Set order status after label print for outbound; false for return. Updating status can trigger WooCommerce/third-party status hooks and emails. The example prevents Smart Send from changing status for either direction.
Action · (WC_Order $order, Smart_Send\Fulfillment\Fulfillment_Result $result) → voidRuns once after all attempted directions and their order-side steps, when at least one direction was fulfilled. A total failure does not fire it. It can carry outbound success alongside return failure. Inspect get_outbound_shipment(), get_return_shipment(), shipments(), get_steps($shipment), get_order_note_id($shipment) and get_warnings($shipment). get_validation_errors($is_return) and get_error_details($is_return) describe failures. See the booked result contracts; do not assume every optional step ran.
Filter · (string $host) → stringRuns when constructing the API client. Default host: https://app.smartsend.io. Return the scheme and hostname only, for example https://app.smartsend.dev; the client appends the API version path. Applies to authentication, pickup lookup and booking. Use credentials for the selected environment; a staging site alone does not change this host.
Filter · (bool $verify) → boolDefault: true. Runs when preparing WordPress HTTP request options. Keep certificate verification enabled for production and sandbox. The example explicitly preserves verification; fix an invalid server certificate or CA configuration rather than disabling verification.
Filter · (string $message, string $level, array $context) → string|null|falseRuns only for entries enabled by the plugin’s logging policy. $level is a WooCommerce log level; $context includes source (smart-send-logistics) and plugin version. Return a rewritten message, or null/false to suppress the entry. This filter cannot enable disabled entries or replace the context by returning an array. The example suppresses debug messages only.
Filter · (string $url) → stringRuns when building the Smart Send row’s configuration guide link on the WordPress plugins screen. Default: https://smartsend.io/woocommerce/configuration/. Return a URL, not HTML. It does not change the API endpoint. The example routes the link to the local Smart Send settings screen.
Filter · (string $url) → stringRuns for the support link on the WordPress plugins screen. Default: https://smartsend.io/support/. Return a URL; the renderer escapes it. The example points to a support page on your own shop; create that page before using the snippet.
Filter · (string $notice_html) → stringRuns when Smart Send displays a major-version update notice in the plugins list. Return trusted, safe HTML: the filtered result is rendered as HTML without another sanitization pass. The example appends static guidance. This is a notice hook, not a shipping/booking hook.

Data contracts

The six order-reading filters above do not receive a return flag. They read the same order data for either direction. smart_send_payload_totals and smart_send_shipment_freetext run only when item data is non-empty. Use smart_send_booking_request for a final direction-specific adjustment.
smart_send_payload_items receives these keys on each row:
smart_send_payload_totals receives floats subtotal_net_amount, subtotal_tax_amount, shipping_net_amount, shipping_tax_amount, total_net_amount, total_tax_amount, plus the string currency. Subtotal excludes shipping and includes non-shipping order fees. Ordinarily subtotal + shipping = total, separately for net and tax; clamping negative values can take precedence. Including-tax amounts are net + tax. Preserve those relationships if changing totals.

Integration reference

This background describes platform hooks used by Smart Send, not additional public Smart Send customization hooks.
The five WooCommerce-prefixed customization hooks above are intended for customizations. The table below explains relevant platform hooks that Smart Send listens to. Their broader contracts belong to WooCommerce; replacing the Smart Send callbacks is not the supported customization route.Smart Send also uses WooCommerce’s Blocks registration and HPOS compatibility mechanisms. These registration details and the experimental Blocks data-attribute filter are implementation details; use the pickup filters above for shared behavior across checkout types.
Smart Send uses these Subscriptions filters at priority 10:Keep these filters’ return values intact when adding your own Subscriptions callbacks. This integration follows the Subscriptions 4.9+ data-copy API; a test of the hook contract is not a test of the commercial plugin’s scheduled renewal/payment engine. See compatibility.
These snippets were executed against the plugin’s real PHP value objects with isolated WordPress/WooCommerce test doubles. Checks cover plain-text labels, result order/limits, automatic versus explicit weight, colli allocation and outbound/return event results. They do not prove carrier acceptance, your other plugins’ interactions or an external system’s delivery. Verify the affected workflow on your configured test shop before deploying.

Metadata

Smart Send stores different data on the order, its shipping items and the catalog products. The keys below describe those records; they are not interchangeable. Prefer the documented hooks and PHP API when changing booking behavior. Use WooCommerce CRUD objects when inspecting metadata so your integration works with both HPOS and traditional order storage. For order reads and intentional updates, follow WooCommerce’s HPOS recipe book : obtain the WC_Order, use its metadata methods, and save through the object. Do not assume orders are WordPress posts.

Shipping-item configuration

These values belong to each WC_Order_Item_Shipping, not to WC_Order. WooCommerce copies them from the chosen Smart Send rate when it creates the shipping item. These are snapshots of the method’s configuration. Editing a zone’s defaults does not rewrite existing shipping items. A service chosen for one booking in the order panel also does not overwrite these values. Read them with $shipping_item->get_meta( $key, true ); use SS_SHIPPING_WC()->method_resolver() when you need the resolved booking service, including the separate mapping for WooCommerce’s native Free Shipping method.

Order configuration and booking results

Read these fields from a WC_Order obtained with wc_get_order(). Delivery configuration is managed by Smart_Send\Delivery\Order_Meta; booking results are managed by Smart_Send\Fulfillment\Shipment_IDs. Shipment IDs identify Smart Send shipments; they are not tracking numbers, PDF URLs or WordPress attachment IDs. The history contains one row per shipment, not one per colli, and does not represent live carrier events. Outbound and return are recorded separately, including when outbound succeeds but the return fails. Use SS_SHIPPING_WC()->shipment_ids()->get( $order, false ) for the latest outbound ID, true for return, or labels( $order ) for the history. The ID reader returns an empty string when no ID is stored. For the distinction between submitted values, filter changes and persistent data, see delivery details and persistence.
Known properties include agent_no, company, address_line1, address_line2, postal_code, city and country. A point may also contain id, carrier, name_line1, name_line2, distance, coordinates and opening_hours; do not assume every optional property is present. Country is an ISO 3166-1 alpha-2 code. Coordinates have latitude and longitude; opening-hour rows have day, opens and closes.Use SS_SHIPPING_WC()->order_meta()->read( $order )->get_pickup_point() for a Smart_Send\Delivery\Pickup_Point or null. The typed object supports accessors and to_array() without requiring callers to depend on every raw property. For booking changes, use smart_send_delivery_details; changing only the raw agent number is not a substitute for resolving and validating a compatible pickup point.
This example represents two units from order item 123 in one colli. That ID is local to the order; it is neither a product ID nor a variation ID.
reference and the optional item name are labels, not item identities. Quantities and order-item IDs are positive integers. Parcel_Plan::from_array() parses the canonical shape; allocation must also be checked against the current order’s item IDs and quantities. Saving through Order_Meta strips the four measurement fields to null. An empty specs list means one parcel containing all items.Do not copy this array unchanged to another order. If items or quantities change, an incompatible saved allocation requires a reset before booking. See delivery details and persistence for allocation rules and the measurements that apply only to a booking.
The Subscriptions integration excludes _ss_shipping_label_id, _ss_shipping_return_label_id and _ss_shipping_labels from renewal metadata. It preserves reusable delivery configuration and remaps parcel allocations to the renewal’s new order-item IDs. If that mapping cannot be verified, ss_shipping_order_parcels can contain an internal unmapped_subscription_plan wrapper that forces an explicit reset. This is an error marker, not a supported parcel-plan format to write._smart_send_renewal_source_item is a temporary internal product-order-item marker with order_id and item_id; the integration removes it after processing the copied items. Do not use it as a persistent integration field._wc_shipment_tracking_items belongs to WooCommerce Shipment Tracking. Smart Send uses that extension’s API for tracking and excludes copied tracking records from renewals; it does not define the extension’s metadata schema. _vc_aio_options belongs to vConnect and is read for compatibility when resolving delivery details. Neither field is a Smart Send-owned write contract.
A submitted order-screen value takes precedence over a stored value before smart_send_delivery_details runs. Following successful booking, the submitted pickup selection and parcel item allocations are saved. The submitted method, parcel weights and dimensions apply only to the current booking. Failed booking does not save those overrides. The filter’s returned object determines what is booked, but it does not itself determine what is saved.For a combined outbound/return request, the submitted parcel plan applies to both directions unless a separate return plan overrides it. A null or empty plan means one parcel with all order items. A single spec with no allocations also includes all items and can supply explicit measurements. Multiple specs require complete allocation: every order unit exactly once, positive whole quantities, and no duplicate order-item row within a spec. Unknown order-item IDs and under-/over-allocation are rejected.

Product customs information

These fields belong to a WC_Product or WC_Product_Variation, not an order or shipping item. Use wc_get_product() and the product’s get_meta() for reading. To save an intentional product-data change, use update_meta_data() followed by the product’s save(); setting a value does not determine whether it is the correct customs classification. Booking reads the current catalog values. A variation’s value takes precedence unless it is '' or null, in which case the parent product’s value is used. The plugin’s product editor supplies the fields on the main product’s Shipping tab; it does not add a separate editor for each variation. These fields are not snapshots copied onto order lines. A deleted catalog product cannot supply its customs data. See the merchant guide to customs fields.

API

Use these PHP APIs inside WordPress to read Smart Send data for an existing WC_Order. Smart Send and a supported WooCommerce version must be active. Call the examples after Smart Send initializes on init at priority 0, for example from an init callback at priority 10 or a later order hook. If you start with an order ID, load it with wc_get_order($order_id) and check that the result is a WC_Order. These reads use WooCommerce’s order and item APIs, so they work with HPOS. They do not book shipments or contact the Smart Send API. The example functions use a my_shop_ prefix; choose your own prefix in your integration.
SS_SHIPPING_WC()->order_meta()->read($order) returns Smart_Send\Delivery\Delivery_Details with the stored pickup point and parcel plan. get_pickup_point() returns a Smart_Send\Delivery\Pickup_Point or null. See the pickup point accessors for the available fields.The helper reads the saved point; it does not find nearby points or refresh its address. If no Smart Send point is stored, it also recognizes the supported vConnect pickup point metadata. An agent number stored on its own does not provide a complete pickup point object. Individual fields can be null.get_parcel_plan() on the same details object returns the saved plan or null. To distinguish a missing plan from an invalid saved allocation, use SS_SHIPPING_WC()->order_meta()->parcel_plan_error($order), which returns an explanation or null. These stored details do not resolve a shipping method; use the next example for that.
There are two different IDs to work with:
  • $order->get_shipping_methods() returns the WooCommerce shipping line items saved on the order. get_method_id() identifies the WooCommerce method, such as smart_send_shipping or free_shipping; get_instance_id() identifies the shipping-zone method instance.
  • SS_SHIPPING_WC()->method_resolver() resolves one Smart Send service ID for each direction, such as postnord_agent or postnord_returndropoff. It reads the order’s Smart Send shipping-line metadata, the current mapping for native free shipping, or supported third-party method mappings.
resolve_outbound($order) and resolve_return($order) return '' when no service can be resolved. If a Smart Send shipping line has no return method configured, resolve_return() throws Smart_Send\Booking\Exceptions\Booking_Exception; the example keeps this distinction in return_error. is_auto_return_enabled($order) reads the automatic-return setting saved on the shipping line.These are the defaults derived for the order. They do not include unsaved choices in the order screen, submitted booking overrides, or changes made by smart_send_delivery_details. They also do not tell you which service a previous shipment was actually booked with; read that from the Booked_Shipment delivered to a booking action.
SS_SHIPPING_WC()->shipment_ids() reads the shipment references saved on the order:direction is outbound or return; booked_at is an ISO 8601 UTC timestamp or null when unavailable. The plugin retains the latest 50 history rows in total across both directions. The latest ID for each direction is stored separately, so an ID can exist even when labels() is empty. A row represents one shipment, which may contain several parcels.These are saved booking references. They do not contain live carrier status, tracking links, documents or the complete Booked_Shipment. To read documents and tracking details returned by a booking, listen to smart_send_booking_completed or smart_send_order_fulfilled and use the booked result accessors. A Fulfillment_Result describes that particular run, including a possible outbound success and return failure; it is not re-created from the order history.
Pickup_Point supplies get_agent_no(), get_company(), get_address_line1(), get_address_line2(), get_postal_code(), get_city(), get_country(), get_carrier(), get_distance(), get_latitude(), get_longitude() and get_opening_hours(). Optional values can be null. Opening hours are a list of day, opens, closes rows: the day is a lowercase English weekday and times use HH:MM:SS. An empty list means unknown opening hours. to_array() gives a serializable representation; it is not permission to trust client-submitted names or addresses.
Booked_Shipment exposes get_shipment_id(), get_carrier(), get_service_code(), is_return(), get_state(), get_booked_at(), get_tracking_code(), get_tracking_url(), parcels(), documents() and codes(). Optional tracking fields can be null. label_document() returns a Shipment_Document or null; do not assume a PDF exists for every result. A document’s download_url() chooses the saved local copy when available, otherwise the Smart Send URL.Fulfillment_Result::get_steps($shipment) reports save_documents (true, false or 'failed'), order_note (boolean), tracking (whether forwarding was requested) and order_status (status or false). A tracking step does not prove that an optional third-party integration was installed or delivered an email.Use get_order_note_id($shipment) for the saved note’s integer ID or null. get_order_note($shipment) supplies note HTML for PHP consumers. The serializable to_array() instead contains an order_note object with an id, not note HTML. Its rows have direction, status (fulfilled/failed), and shipment/steps/warnings or an error. get_error_details($is_return) returns null or an array with message, response_id, fields and html; treat message as plain text and escape output appropriately.