Summary
mam-main has explicit frozen public APIs. Customer mobile apps and ~2K enrolled customer sites depend on them. Internal abstractions wrap them, but their public-facing names and shapes do not change.
When in doubt: wrap, don’t rename.
This article catalogs every frozen contract in one place.
AJAX actions
| Action | Auth | Subsystem | Why frozen |
|---|---|---|---|
local_app_get_phone_data |
both | app-connect | Primary mobile API — every deployed mobile client calls this name |
mam_get_phone_data |
both | app-connect | Modern alias — already in field |
mam_user_roles_cred_handler |
both | user-roles | Legacy login — older mobile clients call this name |
mam_setcron_processor |
both | setcron-manager | FastCron pings this URL |
local_app_get_data |
logged-in | app-settings | Generic admin read |
local_app_save_file |
logged-in | app-settings | Generic admin save |
local_app_save_plist_file |
logged-in | app-settings | iOS plist write |
local_app_update_button_data |
logged-in | app-settings | Button blob update |
local_app_update_image |
logged-in | app-settings | Image upload |
local_app_resize_image |
logged-in | app-settings | Server-side resize |
local_app_generate_launch_screen |
logged-in | app-settings | iOS launch-screen plist generator |
⚠️ Endpoints prefixed local_app_* are public-contract names — mobile clients and admin JS depend on them. Internal handler renames are safe; action names are not.
Option keys
| Option | Owner | Why frozen |
|---|---|---|
local-app-account_code |
MAM_Account_Code_Manager |
Customer enrollment ID — ~2K sites have a value |
local-app-button-array* |
app-settings | Customer-defined button arrays — years of accumulated configs |
local-app-onboarding-status |
setup-wizard | Wizard progress tracker |
local-app-ios-app-name |
publish-app | iOS app name |
local-app-ios-app-channel |
publish-app | TestFlight channel routing |
local-app-images-app-icon |
app-settings | App icon asset |
local-app-app-icon-bg |
app-settings | App icon background |
local-app-images-launch-screen-1 |
publish-app | Launch screen 1 |
local-app-images-launch-screen-2 |
publish-app | Launch screen 2 |
ios_pn_app_bundle_id |
publish-app | iOS bundle id — predates mam_* rename; submitted to App Store |
ios_pn_team_id |
publish-app | Apple team id |
ios_pn_key_id |
push-notification-manager | APNs key id |
ios_app_camera_message / ios_app_gps_message / ios_app_contacts_message |
publish-app | Info.plist usage descriptions |
android_pn_app_bundle_id |
publish-app | Android package name — submitted to Play Store |
mam_ios_version_number / mam_android_version_number |
publish-app | App version |
mam_ios_build_number / mam_android_build_number |
publish-app | Build numbers (must increment per submission) |
ios_app_google_plist |
publish-app | Firebase config for iOS |
ios_app_google_places_api |
publish-app | Google Places API key |
ios_app_launch_screen_bg |
publish-app | Launch screen background |
The local-app-* keys date to before the mam_* rename convention. Renames require a coordinated migration of every site.
The ios_* / android_* keys are tied to App Store / Play Store submissions — renaming risks orphaning customer apps.
Hooks (filters fired)
| Hook | Subscribers | Why frozen |
|---|---|---|
mam_get_phone_data_before_send |
~70 | Primary phone-data extension point |
mam_local_app_data |
0 (active) | Short-circuit — contract preserved even with no current subscribers |
mam_notification_send_message |
1 (action) + 1 (legacy filter) | Primary notification entry; legacy filter retained per SC #175 |
mam_notification_send_pn |
1 | Push channel hook |
mam_notification_list |
68+ | Notification-type registry |
mam_app_settings_get_setting |
100+ call sites | Settings cascade |
mam_app_settings_set_setting |
many call sites | Settings write |
mam_app_settings_get_buttons |
many | Per-role button definitions |
mam_tab_manager |
~43 | Per-button enrichment |
mam_main_add_tab_bar_item_{slug} |
per-button | Dynamic tab-bar dispatch |
mam_main_skip_tab_bar_button |
various | Tab-bar visibility filter |
mam_final_button_settings |
various | Final per-button override |
mam_cron_manager |
25+ | Cron registry |
mam_main_check_initial_form |
various | Form-state validation pass |
mam_main_check_nonce |
various | Nonce check pre-pipeline |
mam_fastlane_settings |
per-plugin | Publish payload extension |
mam_user_logged_in |
various | Post-login hook |
mam_login_handler |
various | Login phase hook |
mam_user_roles_after_create_user |
various | Post-registration hook |
mam_user_roles_save_addl_user_profile_field_{key} |
per-field | Per-field profile save |
mam_add_fields_to_user_profile |
various | Profile-field injection |
mam_plugin_entitlement |
per-plugin | Per-plugin licensing |
mam_form_manager_get_forms_from_plugins |
per-plugin | Custom form registration |
mam_form_manager_process_field_type_{type} |
per-field-type | Custom field-type translation |
mam_form_manager_send_notifications |
various | Form-to-notification bridge |
mam_for_gravity_forms_form_result_form_{form-id} |
per-form | Per-form result envelope |
mam_for_gravity_forms_form_submitted_{form-id} |
per-form | Per-form submission handler |
DB tables
All 6 owned tables. Renamed from wp_tsl_* to wp_mam_* in PR #31, with backward-compat aliasing through MAM_Migration_Tasks::rename_table(). Renaming any of these in place would break every read against the old name — always use the migration framework.
| Current name | Owner repository | Purpose |
|---|---|---|
wp_mam_mail_queue |
MAM_Mail_Queue_Repository |
Outbound email queue |
wp_mam_mail_attachments |
MAM_Mail_Attachments_Repository |
Email attachment storage |
wp_mam_sms_queue |
MAM_Sms_Queue_Repository |
Unified PN+SMS queue |
wp_mam_notification_history |
MAM_Notification_History_Repository |
In-app notification delivery log |
wp_mam_phone_tokens |
MAM_Phone_Token_Repository |
Device registration tokens |
wp_mam_debug_items |
MAM_Debug_Items_Repository |
Debug tracer storage |
What “frozen” means in practice
| You can… | You cannot… |
|---|---|
| Refactor the implementation behind a frozen contract | Rename the contract |
Wrap a frozen option in a manager class (e.g., MAM_Account_Code_Manager) |
Move a frozen option to a custom DB table without migration |
| Add new fields to a frozen JSON shape (additive) | Remove or rename existing fields |
| Add new hooks alongside frozen ones | Rename or remove a frozen hook |
| Rename internal handler classes | Rename the AJAX action they’re bound to |
Rename a frozen DB table via MAM_Migration_Tasks::rename_table() with aliasing |
Rename a frozen DB table with ALTER TABLE directly |
Migration framework
When a rename is unavoidable, use MAM_Migration_Tasks (includes/migrations/):
MAM_Migration_Tasks::register_option_alias( 'old-key', 'new-key' );
MAM_Migration_Tasks::register_option_renames( array(
'old-key-1' => 'new-key-1',
'old-key-2' => 'new-key-2',
) );
MAM_Migration_Tasks::rename_option( 'old', 'new' );
MAM_Migration_Tasks::rename_table( 'wp_old_table', 'wp_new_table' );
Aliases install pre_option / pre_update_option filter pairs so legacy callers (in unmaintained customer-site plugin copies) continue to read the old keys forever.
Related articles
- Plugin: mam-main
- Architecture overview
- Settings cascade overview
- AJAX action: local_app_get_phone_data
- Hook: mam_get_phone_data_before_send
- Hook: mam_notification_send_message
- Hook: mam_app_settings_get_setting
Metadata
| Field | Value |
|---|---|
| Article type | JSON Key Reference |
| Plugin slug | mam-main |
| Applies to plugin version | 2.1.11+ |
| Category | App Settings Reference |
| Audience | PHP developer |
| Last verified | 2026-05-02 |
