What it does
mam-special-offers lets you attach short, dated promotions (“special offers” or “deals”) to listings — GeoDirectory locations, WooCommerce products, or any other parent post type that opts in. Each parent can carry up to four offers. Offers are their own custom post type (special_offers) and link back to a parent post via the special-offer-parent post meta key.
Once attached, offers surface in two places:
- In the mobile app, as a featured-offers feed in the home payload, as a “Special Offers” section on a listing’s detail screen, and as a location-aware “nearby offers” list.
- On the front-end website, as a masonry grid via the
[mam_special_offers]shortcode.
The plugin is configuration-first for content editors — open a listing, fill in up to four offer slots, save — and developer-extensible at the points that usually need it: search radius, parent coordinates, and the “Edit Deals” tab bar button.
Where it appears in the app
When the plugin is active and an offer is published:
- Home screen feed. Offers with
special-offer-featured-listingset toyesare appended to the phone-data payload asspecial_offersandspecial_offers_ids. The home screen also receives ahome_banner_ad_durationof"15"seconds. - Listing detail screen — Special Offers section. Inside a content section of type
special_offers, the plugin renders a card for each offer attached to the listing. Expired offers (end date in the past) are filtered out automatically. - Nearby offers list. When the app fetches the
Special Offerscontent type, the plugin returns offers within a configurable radius (default 5 miles) of$_REQUEST['lat']/$_REQUEST['lon'], sourcing each offer’s coordinates from its parent listing’slat/lonmeta. - Edit Deals tab bar button. On listing detail screens, the
mam_main_add_tab_bar_item_edit_dealsslot is populated with one entry per existing offer plus an “Add Offer” entry, each pointing at the in-app Gravity Form. Requiresmam-gravity-forms-manager. - RSVP flow. When
special-offer-has-rsvpis set on an offer, the detail screen renders an “I’m Going” / “Cancel” button and (after RSVP) a QR confirmation section.
Requirements
- WordPress 6.3+
- PHP 8.1+
- MAM Suite with
mam-mainactivated - MAM Suite plugin entitlement for
mam-special-offers
Optional integrations:
- MAM GeoDirectory — when active, GeoDirectory post types automatically get the Special Offers meta box, and a
special_offerscontent section is registered againstlocal_app_geodirectory. - WooCommerce —
productposts get the meta box too. - MAM Gravity Forms Manager — required for the in-app “Edit Deals” button and for venue-owner self-service offer creation. The plugin auto-registers a Gravity Form named Special Offers: Offers; without the Gravity Forms manager, that form is never built and the tab bar button silently returns empty.
Custom post type
| Property | Value |
|---|---|
| Slug | special_offers |
| Public | false |
| Show in admin UI | false |
| Supports | title, editor, author, thumbnail, excerpt, comments |
The CPT is intentionally hidden from the standard admin menu — offers are created and edited through the Special Offers meta box on the parent post (a GeoDirectory listing, a product, or a special_offers post itself), not through their own list table. Each offer carries a special-offer-parent meta key holding the parent post’s ID.
The meta box renders four numbered tabs (Offer 1 through Offer 4), so a single parent can have up to four concurrent offers. Filling in a previously empty tab creates a new special_offers post on save; checking Delete Offer moves an offer back to draft.
Default offer fields
| Field | Meta key | Notes |
|---|---|---|
| Offer Title | stored as post_title |
Required to create a new offer |
| Subtitle | special-offer-subtitle |
Required to create a new offer (along with title) |
| Details | stored as post_content |
Rich text, sanitized via wp_kses_post |
| Always Available | special-offer-no-dates |
Checkbox; when on, dates are ignored and offer is labelled “Available now” |
| Start Date | special-offer-start-date |
YYYY-MM-DD string |
| End Date | special-offer-end-date |
YYYY-MM-DD string. Offers are dropped from the listing-detail section the day after this date. |
| Offer Image | special-offer-image |
URL, converted to a thumbnail via attachment_url_to_postid() on save |
| URL | special-offer-url |
Optional redirect; if set, the offer’s content_type becomes web and the front-end shortcode links here instead of the parent permalink |
| Delete Offer | special-offer-delete |
Checkbox; sets post_status to draft |
Two more meta keys live on the parent listing rather than on the offer:
| Field | Meta key | Notes |
|---|---|---|
offer-type |
offer-type |
Defaulted to non-event on save if unset |
| Featured listing | special-offer-featured-listing |
Defaulted to yes on save if unset; controls whether the offer appears in the home feed |
Mobile JSON contract
mam-special-offers writes into the mobile JSON payload via the mam_get_phone_data_before_send pipeline. The keys it sets are part of the frozen mobile contract — renaming them will break deployed apps.
| Key | Shape | Notes |
|---|---|---|
special_offers |
array of offer objects | Featured offers (those with special-offer-featured-listing == 'yes'), with the full per-offer shape used by the home feed. |
special_offers_ids |
array of strings | Same offers, IDs only, in menu_order. Used by the Android client. |
home_banner_ad_duration |
"15" |
Always overwritten to 15 seconds whenever this plugin is active. |
For listing detail and nearby-offers responses, each offer object includes id, title, subtitle, excerpt, exp_label, seller_name (the parent’s title), imageurl, distance, optional sale_price / regular_price, and a content_sections array with the offer details table, optional offer rules, and (for RSVP-enabled offers) a check-in section.
Hooks exposed
| Hook | Type | Audience | Purpose |
|---|---|---|---|
mam_specials_get_post_radius |
filter | PHP dev | Override the search radius (miles) for the nearby-offers feed. |
mam_specials_get_post_lat |
filter | PHP dev | Override the latitude resolved for an offer’s parent post. |
mam_specials_get_post_lon |
filter | PHP dev | Override the longitude resolved for an offer’s parent post. |
mam_special_offers_deals_allowed |
filter | PHP dev | Cap on the number of offers per listing exposed by the Edit Deals tab bar button. |
mam_special_offers_hard_skip_tab_bar_button |
filter | PHP dev | Unconditionally skip rendering the Edit Deals button. |
mam_special_offers_skip_tab_bar_button |
filter | PHP dev | Final pass over the Edit Deals button — return modified data or empty array to skip. |
The radius hook, the lat hook, the deals-allowed hook, and the skip hook each have their own dedicated article. See Related articles.
Common tasks
- Add an offer to a listing → Recipe: Add a special offer to a listing
- Let a venue owner add and edit offers from the app → Recipe: Let venue owners manage offers from the app
- Enable RSVP on an offer → Recipe: Enable RSVP for a special offer
- Change how far the “nearby offers” search reaches → Hook: mam_specials_get_post_radius
- Override an offer’s coordinates in code → Hook: mam_specials_get_post_lat
- Cap or relax the number of offers shown on the Edit Deals button → Hook: mam_special_offers_deals_allowed
- Modify or hide the Edit Deals button on specific screens → Hook: mam_special_offers_skip_tab_bar_button
- Render offers on a public website page → use the
[mam_special_offers]shortcode
Verification
This article was last verified against:
- Plugin:
mam-special-offersv2.1 mam-main(required)mam-geodirectory(optional integration — GeoDirectory post types pick up the meta box)mam-gravity-forms-manager(optional integration — required for in-app offer management)
Re-verify whenever a hook is added or removed, the four-offer cap on the meta box changes, the mobile JSON keys named under Mobile JSON contract change, the default search radius (5) changes, or the home_banner_ad_duration override is removed.
Related articles
- Recipe: Add a special offer to a listing
- Recipe: Enable RSVP for a special offer
- Recipe: Let venue owners manage offers from the app
- Hook: mam_specials_get_post_radius
- Hook: mam_specials_get_post_lat
- Hook: mam_special_offers_deals_allowed
- Hook: mam_special_offers_skip_tab_bar_button
Metadata
| Field | Value |
|---|---|
| Article type | Plugin Overview |
| Plugin slug | mam-special-offers |
| Applies to plugin version | 2.1+ |
| Category | Plugin Reference |
| Depends on | MAM Main |
| Works with | MAM GeoDirectory, MAM Gravity Forms Manager (optional, required for in-app offer management), WooCommerce (optional, for product posts) |
| Hooks exposed | mam_specials_get_post_radius, mam_specials_get_post_lat, mam_specials_get_post_lon, mam_special_offers_deals_allowed, mam_special_offers_hard_skip_tab_bar_button, mam_special_offers_skip_tab_bar_button |
| Last verified | 2026-05-01 |
