There was a recent post in the Sure products Facebook page about a designer/developer who had a client that was migrated from Woo to SureCart and was missing the little unfulfilled order bubble, and was there any native way to replicate this? The item to replicate is shown below

There is no native functionality to do just this was the official response and rightly so saying you can see it in one of the copious pages very easily which you can, the one things I have liked about SureCart and continue to dive into is just how great the API and the documents are as well as the support at telling you what you need.
My reply on this question was that this should be possible to replicate easily, as you’d just need to query the unfulfilled orders and if greater than 0, display a bubble with the number next to the menu item. The original poster hadn’t done this sort of thing before, so I said when I get the time I’d take a look as I didn’t think it would take long and I wasn’t wrong in this case.
Background Information
First thing I did was use some of what I already know, we are going to need an API key to query the API I’ve not had much luck getting this out of the database assuming its encrypted (its on my ask support questions if this is possible or to get access to it through a function or filter include. So I would just include this in the code for simplicity.
The next thing I already knew was the SureCart menu system is all built under “sc-dashboard” as a slug. So I used some code to target that and find out what orders are called.
if ( ! defined('SC_DEBUG') ) define('SC_DEBUG', true);
function sc_dbg($label, $val){
if ( SC_DEBUG ) {
error_log(sprintf('[SC DEBUG] %s: %s', $label, wp_json_encode($val)));
}
}
add_action( 'admin_menu', function() {
global $submenu;
// Dump *all* the topβlevel slugs so you can see what SureCart actually uses:
sc_dbg( 'all_submenu_keys', array_keys( $submenu ) );
// If you know the real slug, dump that specific submenu:
$slug = 'sc-dashboard'; // try 'surecart', 'sure-cart', 'sure_cart', etc.
if ( isset( $submenu[ $slug ] ) ) {
sc_dbg( 'surecart_submenu', $submenu[ $slug ] );
} else {
sc_dbg( 'no_submenu_for_' . $slug, true );
}
}, 999 );
PHP
This returned a block like the below
SC DEBUG] raw_sc_dashboard_submenu: [["Dashboard","manage_sc_shop_settings","sc-dashboard","Dashboard"],["Orders","edit_sc_orders","sc-orders","Orders"],["Products","edit_sc_products","sc-products","Products"],["Coupons","edit_sc_coupons","sc-coupons","Coupons"],["Licenses","edit_sc_products","sc-licenses","Licenses"],["Subscriptions","edit_sc_subscriptions","sc-subscriptions","Subscriptions"],["Affiliates","edit_sc_affiliates","sc-affiliates","Affiliates"],["Customers","edit_sc_customers","sc-customers","Customers"],["Shop","manage_options","post.php?post=100&action=edit","Shop"],["Checkout","manage_options","post.php?post=97&action=edit","Checkout"],["Cart","manage_options","site-editor.php?postId=surecart%2Fsurecart%2F%2Fcart&postType=wp_template_part&canvas=edit","Cart"],["Customer Area","manage_options","post.php?post=98&action=edit","Customer Area"],["Custom Forms","manage_options","edit.php?post_type=sc_form","Forms"],["Settings","manage_options","sc-settings","Settings"]]
The Surecart API is really well documented so I also already knew when I responded which API call to use
GET https://api.surecart.com/v1/orders?fulfillment_status[]=processing&limit=1
Authorization: Bearer {API_KEY}
PHP
With some exceptions, we will need to make sure we page through orders as it’s possible on a busy store to have more than one page of unfulfilled orders.
We will also need to cache, so this won’t be live as some stores could have 100s, and we wouldn’t want to call those API calls on every load. I’ve settled for it being refreshed every 5 mins, this can be easily adjusted or removed in the commented code below by removing the transienet
The Code
We aim to add a bubble to the orders tab, with the information based on the queries and key above.

So we query the orders looking for unfulfilled orders, one page at a time with up to 100 orders. We could then nest the JSON responses, cache them and return them to display them in a badge in Surecart.
<?php
/**
* Description: Shows a red badge next to βOrdersβ for processing orders via SureCartβs v1
* Version: 1.0
*/
// βββ CONFIG βββ
// Replace with your real SureCart API key
if ( ! defined( 'SC_API_KEY' ) ) {
define( 'SC_API_KEY', 'YOUR API KEY HERE' );
}
// Toggle API debugging (logs only the request URL and HTTP status):
if ( ! defined( 'SC_DEBUG' ) ) {
define( 'SC_DEBUG', false);
}
// βββ FETCH + CACHE βββ
function sc_get_unfulfilled_count(): int {
// 1) Try cache
$cached = get_transient( 'sc_unfulfilled_count' );
if ( false !== $cached ) {
return (int) $cached;
}
$total = 0;
$page = 1;
$per_page = 100; // fetch 100 orders per request
do {
// build URL with pagination params
$url = add_query_arg( [
'fulfillment_status[]' => 'unfulfilled',
'limit' => $per_page,
'page' => $page,
], 'https://api.surecart.com/v1/orders' );
if ( SC_DEBUG ) {
error_log( "[SC DEBUG] Requesting page $page: $url" );
}
$resp = wp_remote_get( $url, [
'headers' => [
'Authorization' => 'Bearer ' . SC_API_KEY,
'Accept' => 'application/json',
],
'timeout' => 5,
] );
if ( is_wp_error( $resp ) ) {
if ( SC_DEBUG ) {
error_log( '[SC DEBUG] API error: ' . $resp->get_error_message() );
}
break;
}
$code = wp_remote_retrieve_response_code( $resp );
if ( SC_DEBUG ) {
error_log( "[SC DEBUG] HTTP status for page $page: $code" );
}
if ( 200 !== $code ) {
break;
}
$body = wp_remote_retrieve_body( $resp );
$data = json_decode( $body, true );
// count how many items on this page
$count_on_page = isset( $data['data'] ) && is_array( $data['data'] )
? count( $data['data'] )
: 0;
if ( SC_DEBUG ) {
error_log( "[SC DEBUG] Page $page returned $count_on_page items" );
}
$total += $count_on_page;
$page++;
} while ( $count_on_page === $per_page ); // if we got a full page, there might be more
// cache and return
set_transient( 'sc_unfulfilled_count', $total, 5 * MINUTE_IN_SECONDS ); //remove if you don't want caching.
return $total;
}
//
// βββ INJECT BADGE βββ
add_action( 'admin_menu', function() {
$count = sc_get_unfulfilled_count();
if ( $count < 1 ) {
return;
}
global $submenu;
if ( ! isset( $submenu['sc-dashboard'] ) ) {
return;
}
foreach ( $submenu['sc-dashboard'] as & $item ) {
if ( stripos( $item[0], 'orders' ) !== false ) {
$item[0] .= sprintf(
' <span class="awaiting-mod"><span class="count">%d</span></span>',
$count
);
break;
}
}
}, 100 );
// βββ STYLING βββ
add_action( 'admin_head', function() {
echo '<style>
.awaiting-mod .count { font-weight:600; padding:0 6px; }
</style>';
} );
PHP
Now the code above works and displays the image below, but it’s not battle tested, and you are likely to need to adjust caching times and even polling numbers based on your site and server, but it does go to show what is possible with an API call and a few known variables and the reason we know so much is because of how good the documentation is.
