Developers
StitchPress is extended through WordPress actions and filters. Every hook name starts with stp_. Where a setting and a filter cover the same value, the settings screen hooks that filter at the default priority (10). Hook the same filter at priority 11 or higher and your value wins over the setting.
Hooks and filters
Arguments are listed in order. "Action" means do_action, everything else is a filter.
Extension points
| Hook | Arguments | Use |
|---|---|---|
stp_settings_schema |
$schema |
Add options: name => type, default, label, description, section, choices, sanitize. An entry with a section gets a field on the settings screen. Read with STP_Settings::get(). |
stp_settings_sections |
$sections |
Add settings sections: id => title, description, priority. |
stp_admin_menu (action) |
$parent, $cap |
Add a screen under the StitchPress menu with add_submenu_page( $parent, ... ). |
stp_admin_submenus |
$entries |
Declarative alternative: a list of slug, title, menu_title, capability, callback. |
stp_rest_routes |
$routes, $namespace |
Add REST routes under stitchpress/v1 (see below). |
stp_rest_init (action) |
$namespace |
After StitchPress routes are registered. |
stp_email_classes |
$emails |
Add WC_Email classes: id => instance or class name. |
stp_email_templates |
$templates |
Email template map: id => html and plain paths relative to templates/. |
stp_module_dirs |
$dirs |
Folders scanned for StitchPress modules. |
stp_modules_loaded (action) |
$classes |
After every module's register() ran. |
stp_event (action) |
$name, $context |
Every StitchPress domain event (see Events). |
stp_log_debug_enabled |
$on |
Force debug logging on or off. |
Templates and methods
| Hook | Arguments | Use |
|---|---|---|
stp_decoration_methods |
$methods |
The method registry. Add a method (label, palette label, colors per element, accepted artwork, pricing dimensions, setup fee unit, sheet fields, estimators) without editing core. |
stp_resolve_template_for_product |
$resolved, $product_id |
The template a product resolves to. Return null to disable decoration on a product. |
stp_template_category_terms |
$terms, $parent_id |
Reorder or filter the categories consulted during resolution. |
stp_template_config |
$config, $template_id |
A decoded template config on read. |
stp_template_default_config |
$config |
The skeleton for new templates. |
stp_template_saved (action) |
$template_id, $config |
After a template is stored. |
stp_template_config_invalid (action) |
$template_id, $reason |
A stored config could not be decoded. |
stp_pricing_models_for_method |
$models, $method_key, $definition |
Pricing models a method may use. |
stp_setup_fee_units |
$units |
Accepted setup-fee counting units. |
stp_palettes_options |
$options |
Palettes offered in the template editor. |
Pricing and setup fees
| Hook | Arguments | Use |
|---|---|---|
stp_price_for_item |
$amount, $item, $spec, $config, $qty |
Price of one placement, before tiers. |
stp_price_for_spec |
$amount, $spec, $config, $qty |
Final per-unit decoration charge. A change adds an "Adjustment" row to the breakdown. |
stp_pricing_breakdown |
$lines, $spec, $config, $qty |
Breakdown rows (label, amount). Change totals through stp_price_for_spec so rows still add up. |
stp_pricing_model_lines |
$lines, $model, $items, $block, $spec, $config, $qty |
Price a custom pricing model. |
stp_pricing_config_for_record |
$config, $spec, $product_id, $live_config |
The config a design is priced against (default: the template it was captured on). |
stp_pricing_boot_data |
$out, $config |
Pricing data sent to the designer. |
stp_pricing_cart_item_priced (action) |
$cart_item_key, $surcharge, $base, $spec, $cart |
After a cart line is repriced. |
stp_pricing_missing_config (action) |
$method, $spec, $config |
A design's method has no pricing block; it is priced at 0. |
stp_pricing_unknown_model (action) |
$model, $method, $spec |
A stored pricing model has no implementation. |
stp_pricing_template_drift (action) |
$spec_template_id, $live_id, $product_id, $spec |
The product now resolves to a different template than the design's. |
stp_pricing_template_unresolved (action) |
$product_id, $spec_template_id, $spec |
The design's template is gone; priced from the product's current one. |
stp_pricing_error (action) |
$e, $context |
Pricing caught an exception (the cart never fatals). |
stp_setup_fee_item_signatures |
$signatures, $unit, $item |
What counts as one setup unit for an item. |
stp_setup_fee_units_count |
$count, $method_key, $signatures, $cart |
Setup units charged per method. |
stp_setup_fee_waived |
$waived, $method_key, $decorated_subtotal, $fee, $cart |
Whether the setup fee is waived. |
stp_setup_fee_amount |
$amount, $method_key, $count, $fee, $cart |
The setup fee amount. |
stp_setup_fee_label |
$label, $method_key, $unit, $count |
The fee line label. |
stp_setup_fee_taxable |
$taxable, $method_key, $cart |
Whether the fee is taxable (default: store tax setting). |
stp_setup_fee_tax_class |
$tax_class, $method_key, $cart |
Tax class for the fee. |
stp_setup_fee_conflict (action) |
$method_key, $winner, $loser, $cart |
Two templates in one cart set different fees; the higher wins. |
stp_underbase_charges_screen |
$charges, $method_key |
Whether a definite underbase is billed as an extra screen. |
Designer and cart
| Hook | Arguments | Use |
|---|---|---|
stp_capture_usable_config |
$usable, $resolved |
What the designer may offer on a product. Return null to hide it. |
stp_capture_boot_data |
$boot, $usable, $product |
The data handed to the designer script. |
stp_capture_launcher_label |
$label, $product |
The launcher button text (default "Design yours"; the Launcher button label setting overrides it). |
stp_capture_display |
$display, $usable, $modes |
inline (a card in the product page, the default when visual placement is on) or modal (a popup). |
stp_capture_launcher_blocks |
$blocks |
Block names the launcher is injected into on block themes. |
stp_inline_boot |
$inline |
Print the boot module inline (default) or as a file. |
stp_capture_modes_for_method |
$modes, $method_key, $definition |
Text and artwork modes per method. |
stp_capture_colors_required |
$required, $mode, $cardinality, $definition |
Whether an element must carry a color. |
stp_capture_popular_tier |
$tier, $entries, $palette_id |
Palette tier shown before "More colors". Return '' to show the first 24 colors instead. |
stp_capture_record |
$record, $raw, $usable |
A validated design record, before the save-path check. |
stp_capture_item_summary |
$summary, $item |
One-line summary of a placement in the cart and order. |
stp_capture_show_price_row |
$show, $record |
Hard off switch for the cart price rows. |
stp_capture_price_breakdown |
$mode, $record |
Cart detail: rows, total or off. |
stp_capture_show_edit_link |
$show, $cart_item, $cart_item_key |
Whether a cart line offers "Edit design". |
stp_capture_edit_link_label |
$label, $cart_item |
The edit link text. |
stp_capture_ajax_success |
$payload, $cart_item_key, $cart_item |
AJAX add-to-cart success payload. |
stp_capture_ajax_error |
$payload, $code |
AJAX add-to-cart error payload. |
stp_capture_cart_line_rejected (action) |
$cart_item_key, $error_code, $product_id |
A cart line was removed because its design failed validation. |
stp_capture_cart_line_replaced (action) |
$old_key, $new_key, $product_id |
A re-edited cart line replaced the old one. |
stp_capture_spec_write_failed (action) |
$item, $code |
A design record could not be saved onto an order line. |
stp_capture_estimate_failed (action) |
$e, $item, $record |
No estimate could be produced for an order line. |
stp_capture_variation_template_ignored (action) |
$variation_id, $variation_template_id, $parent_template_id |
A variation-level template was ignored. |
stp_save_design_rate_limit, stp_save_design_global_limit, stp_save_design_max_bytes |
$value |
Limits for guest "Save design": per client per hour (30), shop-wide per hour (600), bytes (64 KB). |
Uploads and artwork
| Hook | Arguments | Use |
|---|---|---|
stp_artwork_max_bytes |
$max |
Artwork size limit (default 10 MB). |
stp_artwork_rate_limit |
$limit |
Uploads per client per hour (default 20). |
stp_artwork_client_ip |
$ip |
The client IP the upload and Save design limits count against (default REMOTE_ADDR). |
stp_artwork_uploaded (action) |
$rel, $result |
After an artwork file is stored. |
stp_artwork_gc_max_age |
$seconds |
Age before unreferenced artwork is deleted (default 30 days). |
stp_artwork_gc_delete |
$delete, $path, $basename |
Last chance to keep a file the cleanup is about to delete. |
Palettes and fonts
| Hook | Arguments | Use |
|---|---|---|
stp_palette_boot_entries |
$entries, $palette_id |
Palette entries sent to the designer. |
stp_palette_imported (action) |
$palette_id, $imported, $skipped |
After a CSV import. |
stp_palette_deleted (action) |
$palette_id |
After a palette is deleted. |
stp_font_max_bytes |
$max |
Font size limit (default 5 MB). |
stp_font_faces_css |
$css, $fonts, $font_ids |
The @font-face CSS StitchPress prints. |
stp_fonts_changed (action) |
none | A font was added, deleted, reordered or toggled. |
Production estimates
| Hook | Use |
|---|---|
stp_estimates_enabled |
Estimates on or off. |
stp_estimate_customer_visible |
Whether the estimate is written onto the order and emails. |
stp_estimate_for_record |
The finished estimate record ($estimate, $spec, $config, $qty, $product_id) before it is stored. |
stp_estimate_meta_description |
The one-line customer-visible summary. |
stp_estimate_warning_text |
A rendered warning ($text, $key, $data, $method). |
stp_estimate_underbase |
The underbase conclusion for a line. |
stp_estimates_boot_data |
Estimator data sent to the designer. |
stp_estimate_error (action) |
The estimator caught an exception. |
stp_estimate_band_pct, stp_emb_density, stp_emb_stitches_per_char, stp_emb_min_text_height, stp_emb_max_area, stp_emb_artwork_coverage, stp_emb_detail_tones, stp_artwork_min_dpi, stp_sp_max_colors, stp_underbase_mode, stp_underbase_attribute, stp_underbase_dark_terms, stp_tier_nudge, stp_tier_nudge_within |
The constants behind the settings of the same names. |
stp_estimate_advance_ratio, stp_estimate_cap_ratio, stp_estimate_height_exponent, stp_estimate_alpha_cut, stp_estimate_white_cut, stp_estimate_max_analysis_pixels |
Text-measurement and artwork-analysis constants. |
Previews
| Hook | Use |
|---|---|
stp_preview_enabled |
Turn cart preview pictures off ($enabled, $record). |
stp_preview_format |
webp or png. |
stp_preview_width |
Width in pixels. |
stp_preview_max_decode_pixels |
Pixel ceiling for one render. |
stp_preview_gc_max_age |
Age before unused previews are deleted. |
Proofs
| Hook | Arguments | Use |
|---|---|---|
stp_proofs_hold_order |
$hold, $order |
Kill switch for the production gate. |
stp_proofs_gate_statuses |
$statuses, $order |
Statuses whose arrival puts an order on the gate (default processing, on-hold, completed). |
stp_proofs_terminal_statuses |
$statuses, $order |
Statuses in which the proof workflow does nothing. |
stp_proof_token_ttl_days |
$days |
Link lifetime after upload (default 60). |
stp_proof_max_bytes |
$max |
Proof file size limit (default 20 MB). |
stp_proof_client_ip |
$ip |
The client IP recorded on decisions and counted by the decision limit. |
stp_proof_email_subject, stp_proof_email_body |
see Emails | The Proof ready email. |
stp_proof_confirm_customer |
$send, $order, $decision, $round |
Whether to send the customer confirmation. |
stp_proof_confirm_subject, stp_proof_confirm_body |
The customer confirmation. | |
stp_proof_notify_admin_on_approve |
$notify, $order |
Whether to email the shop on approval. |
stp_proof_admin_email |
$email, $order |
Shop notice recipient. |
stp_proof_admin_notification |
$mail, $order, $round, $decision |
Shop notice: to, subject, body. |
stp_proof_contact, stp_proof_logo |
Contact line and logo on the proof page. | |
stp_proof_round_added (action) |
$order, $number |
A round was uploaded. |
stp_proof_sent (action) |
$order, $number, $email |
A round was emailed. |
stp_proof_approved (action) |
$order, $round, $data |
A round was approved. |
stp_proof_changes_requested (action) |
$order, $round, $data |
The customer asked for changes. |
stp_proof_hold_released (action) |
$order, $reason |
The gate was canceled without an approval. |
Job Jacket and production queue
| Hook | Use |
|---|---|
stp_sheet_render_order |
The rendered Job Jacket for an order ($html, $order). |
stp_sheet_render_item |
One line item's section ($html, $item, $spec). |
stp_sheet_css |
The Job Jacket stylesheet. |
stp_sheet_field_labels |
Production field labels. |
stp_sheet_estimate_warning_labels |
Operator wording for estimate warnings. |
stp_sheet_item_proof_banner |
The per-item proof banner. |
stp_sheet_proof_width |
Width of the proof picture. |
stp_sheet_inline_image_exts |
Artwork types shown inline. |
stp_sheet_arc_preview_max_chars |
Longest text the arc preview draws. |
stp_production_field_choices |
Fixed choices on the Confirm production values form. |
stp_queue_statuses |
Statuses the production queue covers. |
stp_queue_scan_limit |
How many recent decorated orders the queue inspects. |
stp_queue_proof_state |
The queue's proof cell. |
Skins
| Hook | Arguments | Use |
|---|---|---|
stp_skin_url |
$url, $key |
URL of the skin file to load; empty string loads none. See Skins. |
stp_skin_choices |
$choices |
Skins listed on the settings screen. |
The full map, including names reserved for upcoming features, is includes/hooks.php in the plugin source.
Hooks added in 1.0.0: stp_settings_export_keys, stp_status_checks, stp_wizard_steps, stp_bulk_assign_done, stp_production_statuses, stp_production_status_changed, stp_webhook_topics, stp_reorder_record, stp_export_columns, stp_variation_view_image, stp_design_saved, stp_privacy_data, stp_retention_days, stp_license_status, stp_demo_created. Arguments are documented in includes/hooks.php.
Events
STP_Log::event( $name, $context ) fires the stp_event action with an event name and a context array of IDs and states, never customer text. Listen to it for webhooks, integrations or your own logging:
add_action( 'stp_event', function ( $name, $context ) {
if ( 'proof.approved' === $name ) {
// $context['order_id'], $context['round']
}
}, 10, 2 );
Event names are listed in includes/hooks.php (design.ordered, proof.sent, proof.approved, proof.changes_requested, production.status_changed, design.saved, upload.rejected, settings.imported, privacy.erased, license.changed). In this build the proof workflow fires its own actions (stp_proof_sent, stp_proof_approved, stp_proof_changes_requested); use those until the events are wired.
REST API
All StitchPress routes live under one namespace, stitchpress/v1. Routes are added through the stp_rest_routes filter, not by calling register_rest_route() directly:
add_filter( 'stp_rest_routes', function ( $routes ) {
$routes[] = array(
'route' => '/orders/(?P<id>\d+)/designs',
'args' => array(
'methods' => 'GET',
'callback' => 'my_designs_callback',
'permission_callback' => function () {
return current_user_can( 'manage_woocommerce' );
},
),
);
return $routes;
} );
args may be one endpoint array or a list of them, as WordPress accepts. A route without a permission_callback is refused and logged as an error, so a forgotten capability check cannot ship. A public route must say so explicitly with '__return_true' and do its own nonce and rate-limit checks.
StitchPress's own routes need manage_woocommerce (an application password or a WooCommerce REST API key of a user who has it; 401 or 403 otherwise):
GET /orders: decorated orders, newest first. Argumentsstatus,production_status,after(date-time),page,per_page(up to 100). TheX-WP-TotalandX-WP-TotalPagesheaders count every matching order. Decorated orders are found by reading orders newest first, at most 10,000 for one page (filterstp_rest_orders_scan_limit); when that limit stops a page before it is full, the response has the headerX-STP-Truncated: true. Narrow the request withafterorstatusand ask again.GET /orders/<id>: the order document (lines with their design records, proof state, production status and history).GET /orders/<id>/designs: the design record of each decorated line, keyed by line id.GET /orders/<id>/sheet: the production sheet HTML.POST /orders/<id>/production: set the production status (status).
Webhooks
Three WooCommerce webhook topics: stitchpress.design_ordered (a decorated order is paid, once per order), stitchpress.proof_approved and stitchpress.production_status. Add them under WooCommerce > Settings > Advanced > Webhooks like any WooCommerce webhook; the secret, the X-WC-Webhook-Signature header (base64 of HMAC-SHA256 of the body with the secret) and the delivery log are WooCommerce's.
The body is the document GET /stitchpress/v1/orders/<id> returns, plus three fields:
event: the topic.event_id: a UUID (v4) for this event. It is the same on every retry of the event, so store it and skip a body whoseevent_idyou have already processed. WooCommerce'sX-WC-Webhook-Delivery-IDheader changes on every attempt and is not a de-duplication key.occurred_at: when the event happened, in UTC (2026-10-07T14:03:00Z), not when this attempt was sent.
Retries. WooCommerce sends each delivery once. When one of these topics gets a connection error or a status outside 200 to 299, StitchPress sends it again after 1, 5 and 30 minutes (filter stp_webhook_retry_delays), then logs that it gave up.
WooCommerce disables a failing webhook. Every failed attempt, retries included, adds one to the webhook's failure count; a success (status 200 to 302) resets it to zero. When a delivery fails while the count is already above 5 (filter woocommerce_max_webhook_delivery_failures), WooCommerce sets the webhook to Disabled. One event that fails every attempt counts 4 failures, so two such events in a row disable the webhook, and StitchPress does not retry for a disabled webhook. Re-enabling it in the settings does not reset the count, so the next failure disables it again until one delivery succeeds. Keep your endpoint answering 2xx quickly and do the work after replying.
Logging
StitchPress logs through WooCommerce's logger with the source stitchpress. Read the logs under WooCommerce > Status > Logs and pick a stitchpress file (or source).
- Errors and warnings are always written.
- Debug lines are written only when Debug log is on (StitchPress settings > Advanced) or when
stp_log_debug_enabledreturns true. Leave it off unless support asks for it. - Anything that looks like an email address is masked as
[email]before a line is written.
From your own code:
STP_Log::warning( 'Something to look at', array( 'order_id' => 123 ) );
STP_Log has debug(), info(), warning(), error() and event(). Pass IDs and states in the context array, not customer text.
Template overrides
StitchPress renders its storefront, proof page and Job Jacket markup in PHP, not from template files, so there is nothing to copy into your theme in this build. Change output with the filters above (stp_sheet_render_item, stp_sheet_css, stp_proof_email_body and so on) and the look of the designer with a skin.
The override folder is reserved: templates that StitchPress renders through STP_Email_Registry::render() are looked up first in wp-content/themes/<your-theme>/stitchpress/, then in the plugin's templates/ folder, the same way WooCommerce template overrides work.
Email templates ship under templates/emails/ as an HTML and a plain-text file per email; copy one to <your-theme>/stitchpress/emails/ with the same file name to override it.
Product setup
Products that arrive by import, REST or a feed plugin are set up at the end of the request (see Set up a product for decoration).
do_action( 'stp_setup_product', $product_id )queues a product from your own importer.stp_setup_should_run(filter,bool $run, int $product_id, string $source) skips products.stp_setup_result(filter,array $report, WC_Product $product, array $args) changes what is written, or returns aWP_Errorto stop.stp_setup_done(action,int $product_id, array $report) runs after each setup.STP_Product_Setup::run( $product_id, array( 'dry' => true ) )returns the report without writing.- REST:
GETandPOST stitchpress/v1/products/<id>/setup(dry,force), for users who can manage WooCommerce and edit that product.
Product meta: _stp_decorate (yes or no), _stp_product_views (view key to attachment ID), _stp_setup (the last report).
Adding a module
Code that ships inside StitchPress is organised as modules: a file includes/admin/, includes/commerce/ or includes/release/ named class-stp-<name>.php, defining STP_<Name> with a public static function register(). StitchPress loads every module on plugins_loaded after its core has started. This is for StitchPress itself; for site-specific changes, use the hooks above from your theme or a small plugin.