diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 4947039..f3a5cf9 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -29,5 +29,6 @@ "statusLine": { "type": "command", "command": "npx -y @owloops/claude-powerline@latest --style=powerline" - } + }, + "outputStyle": "jacek" } diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index 5f7620a..5efd7b0 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -323,7 +323,7 @@ }, "index.php": { "type": "-", - "size": 3891, + "size": 4210, "lmtime": 1771198110809, "modified": true }, @@ -622,15 +622,15 @@ "products": { "main_view.php": { "type": "-", - "size": 49238, - "lmtime": 1771441425576, + "size": 64362, + "lmtime": 1771717398898, "modified": false }, "product_history.php": { "type": "-", - "size": 13214, - "lmtime": 1769467103988, - "modified": true + "size": 13326, + "lmtime": 1771717401282, + "modified": false } }, "site": { @@ -656,8 +656,8 @@ "campaigns": { "main_view.php": { "type": "-", - "size": 20532, - "lmtime": 1771626632932, + "size": 21607, + "lmtime": 1771717394441, "modified": false } }, @@ -678,8 +678,8 @@ "campaign_terms": { "main_view.php": { "type": "-", - "size": 81281, - "lmtime": 1771441788473, + "size": 81511, + "lmtime": 1771717410322, "modified": false } }, @@ -694,8 +694,16 @@ "facebook_ads": { "main_view.php": { "type": "-", - "size": 10785, - "lmtime": 1771619352316, + "size": 10806, + "lmtime": 1771717403326, + "modified": false + } + }, + "logs": { + "main_view.php": { + "type": "-", + "size": 5948, + "lmtime": 1771717396318, "modified": false } } diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 9a85c4c..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,45 +0,0 @@ -# Repository Guidelines - -## Project Structure & Module Organization -adsPRO is a custom PHP MVC app. Keep business logic in: -- `autoload/controls/` (`class.*.php`): request handling (`\controls\*`) -- `autoload/factory/` (`class.*.php`): DB access via Medoo (`\factory\*`) -- `autoload/view/` (`class.*.php`): view composition (`\view\*`) -- `autoload/services/`: external integrations (Google Ads, OpenAI, Claude) -- `templates/`: PHP templates rendered with `\Tpl::view()` -- `migrations/`: SQL migrations (`NNN_description.sql` + optional `demo_data.sql`) -- `layout/`: SCSS/CSS assets (`style.scss` -> `style.css`) - -Main entry points are `index.php`, `ajax.php`, `api.php`, `cron.php`, and `install.php`. - -## Build, Test, and Development Commands -- `php -S 127.0.0.1:8000` - run a local PHP server from repo root. -- `php install.php` - apply pending DB migrations. -- `php install.php --with_demo` - apply migrations and demo data. -- `php install.php --force` - re-apply tracked migrations. -- `php -l autoload/controls/class.Campaigns.php` - lint a changed PHP file. - -There is no dedicated build pipeline; frontend dependencies are committed in `libraries/`. SCSS is typically compiled via VS Code Live Sass Compiler. - -## Coding Style & Naming Conventions -- Follow existing PHP style: spaces inside parentheses (`if ( $x )`), braces on new lines. -- Use `PascalCase` class names, lowercase namespaces (`\controls`, `\factory`), and `snake_case` for methods/variables/DB columns. -- Controllers/factories are conventionally `static public function ...`. -- Keep route/module naming aligned with URL pattern `/module/action/...`. - -## Testing Guidelines -No first-party automated test suite is maintained in this repo. Validate changes with: -- PHP lint on edited files. -- Manual UI checks for affected `templates/` views. -- Endpoint checks for `ajax.php`, `api.php`, or `cron.php` paths you touched. -- Migration dry run on a non-production database when schema is changed. - -Document manual test steps and outcomes in each PR. - -## Commit & Pull Request Guidelines -- Use concise Polish commit messages with prefixes seen in history: `feat:`, `fix:`, `update:`. -- Keep commits focused (feature, refactor, migration, UI tweak). -- PRs should include: scope summary, linked issue/task, migration notes, manual test checklist, and screenshots for UI changes. - -## Security & Configuration Tips -`config.php` contains environment credentials. Do not introduce new secrets in commits or PR discussions, and treat any credential change as a coordinated ops task. diff --git a/TODO.md b/TODO.md deleted file mode 100644 index e69de29..0000000 diff --git a/temp_fb_authentication.html b/temp_fb_authentication.html deleted file mode 100644 index d710c5c..0000000 --- a/temp_fb_authentication.html +++ /dev/null @@ -1,121 +0,0 @@ - - -
Marketing API calls require an access token to be passed as a parameter in every API call.
- -See Access Tokens for Meta Technologies for more information on the various types of access tokens.
-You can get a user access token using the Graph API Explorer. To learn how to use the Graph API Explorer to make API calls, see the Graph API Explorer Guide.
- -ads_read and/or ads_management).To get more information in the token you just generated, click on the information icon (i) in front of the token to open the Access Token Info table, which displays some basic information about the token. Click Open in Access Token Tool to be redirected to the Access Token Debugger.
- -While debugging, you can check:
- -Check your new token’s properties using the Access Token Debugger. It should have a longer expiration time, such as "60 days", or "Never" under Expires. See Long-Lived Access Token for more information.
- -A system user access token is a type of access token that is associated with a system user account, which is an account that is created in Meta Business Manager for the purpose of managing assets and calling the Marketing API. System user access tokens are useful for server-to-server interactions where there is no user present to authenticate. They can be used to perform actions on behalf of the business, such as reading and writing business data, managing ad campaigns, and other ad objects.
- -One benefit of using a system user access token is that it does not expire, so it can be used in long-running scripts or services that need to access the Marketing API. Additionally, because system user accounts are not tied to a specific individual, they can be used to provide a level of separation between personal and business activity on Meta technologies.
- -System user tokens are also less likely subject to invalidation for other reasons compared to the long-lived user access tokens.
- -See System Users for more information.
-After the owner of an ad account you are going to manage clicks the Allow button when you prompt for permissions, they are redirected to a URL that contains the value of the redirect_uri parameter and an authorization code:
-http://YOUR_URL?code=<AUTHORIZATION_CODE> -
You can then build the URL for an API call that includes the endpoint for getting a token, your app ID, your site URL, your app secret, and the authorization code you just received:
-
-https://graph.facebook.com/v25.0/oauth/access_token?
- client_id=<YOUR_APP_ID>
- &redirect_uri=<YOUR_URL>
- &client_secret=<YOUR_APP_SECRET>
- &code=<AUTHORIZATION_CODE>
-The API response should contain the generated access token:
- -If the API is to be invoked by a system user of a business, you can use a system user access token.
- -You can debug the access token, check for expiration, and validate the permissions granted using the access token debugger or the programmatic validation API.
-Your token should be safely stored in your database for subsequent API calls. Moving tokens between your client and server must be done securely over HTTPS to ensure account security. Read more about the implications of moving tokens between your clients and your server.
- -You should regularly check for validity of the token, and if necessary, prompt for permissions renewal. Even a persistent token can become invalid in a few cases, including the following:
- -As user access tokens can be invalidated or revoked anytime for some reasons, your app should expect to have a flow to re-request permission from users. Check the validity of the user token when they start your app. If necessary, re-run the authentication flow to get an updated token.
- -If this is not possible for your app, you may need a different way to prompt for permissions. This can happen in cases where the API calls are not directly triggered by a user interface or are made by periodically run scripts. A possible solution is to send an email with instructions.
-To ensure the security of user credentials and access tokens, you should adhere to the following best practices:
- -The authorization process verifies the users and apps that will be accessing the Marketing API and grants them permissions.
-In your app's dashboard, you can set roles for yourself or team members as necessary: Admin, Developer, Tester.
- -Note: Depending on your intended use case, you may need to submit your app for review to gain access to specific permissions related to ad management.
-Business apps are subject to an additional layer of Graph API authorization called access levels. During App Review, your app must also request specific permissions and features.
-All developers must follow all Meta Platform Terms and Developer Policies. Calls on ANY access level are against production data.
-Permissions and features for apps have two different access levels: standard access and advanced access (Note: The use of the term "standard access" here is not related to the Ads Management Standard Access feature.) The advanced access level of Ads Management Standard Access still requires an app to pass through review in order to have access to the feature.
| Marketing API Access | Ads Management Standard Access | Action |
|---|---|---|
Development access - | Standard access - | By default - |
Standard access - | Advanced access - | Apply in App Dashboard - |
To check your current access level, go to App Dashboard > App Review > Permissions and Features.
- -The permissions you should request change depending on which API you want to access.
- -If your app is only managing your ad account, standard access to the ads_read and ads_management permissions are sufficient. If your app is managing other people's ad accounts, you need advanced access to the ads_read and/or ads_management permissions. See all available permissions for business apps.
The features you should request change depending on how you want to use our APIs. If you're managing ads, a common feature to request is Ads Management Standard Access. See all available features for business apps.
- -| Feature Access Level | Description |
|---|---|
| - | Business apps are automatically approved for standard access for all permissions and features available to the Business app type. - -Use this option if you're getting started. You can build end-to-end workflows before requesting full permissions, and you can access an unlimited number of ad accounts. - -Some API calls may not be available with standard access because they may belong to multiple accounts or the affected account can't be identified programmatically. - |
| - | Advanced access must be approved through the App Review process on an individual permission and feature basis. - -
After you submit your information, Meta responds with an approval or denial and additional information if your app is not qualified for standard access. - -If you're approved for advanced access, you need to do the following to maintain your status: - -
|
The table below shows how standard and advanced access levels impact the Ads Management Standard Access feature.
-| -Standard Access - | -Advanced Access - | |
|---|---|---|
Account Limits - | Manage an unlimited number of ad accounts. App admins or developers can make API calls on behalf of ad account admins or advertisers. - | Manage an unlimited number of ad accounts, assuming you get |
Rate Limits - | Heavily rate-limited per ad account. For development only. Not for production apps running for live advertisers. - | Lightly rate limited per ad account. - |
Business Manager - | Limited access to Business Manager and Catalog APIs. No Business Manager access to manage ad accounts, user permissions and Pages. - | Access to all Business Manager and Catalog APIs. - |
System User - | Can create 1 system user and 1 admin system user. - | Can create 10 system users and 1 admin system user. - |
Page Creation - | Cannot create Pages through the API. - | Cannot create Pages through the API. - |
In order to get advanced access of Ads Management Standard Access, your app needs to meet these requirements:
- -If you're managing someone's ads, use the scope parameter to prompt them for the ads_management or ads_read permissions. Your app gets access when they click Allow.
-https://www.facebook.com/v25.0/dialog/oauth?
- client_id=<YOUR_APP_ID>
- &redirect_uri=<YOUR_URL>
- &scope=ads_management
-Note: When inputting the YOUR_URL field, put a trailing / (for example, http://www.facebook.com/).
| Use Case | What To Request |
|---|---|
You want to read and manage ads for ad accounts you own or have been granted access to by the ad account owner. - |
|
You want to read ad reports for ad accounts you own or have been granted access to by the ad account owner. - |
|
You want to pull ad reports from a set of clients and to both read and manage ads from another set of clients. - |
|
Business verification is a process that allows us to verify your identity as a business entity, which we require if your app will access sensitive data. Learn more about the Business Verification process.
-To effectively utilize the Marketing API, users must follow some key steps to set up their environment and gain access to the API's features. This section outlines the prerequisites necessary for getting started.
-To manage your ads through the Marketing API, you must have an active ad account. This account is crucial not only for running campaigns but also for managing billing settings and setting spending limits. An ad account allows you to track your advertising expenses, monitor performance, and optimize your campaigns effectively.
Locating your ad account number can be done through the Meta Ads Manager.
See Register as a Meta Developer for more information.
See Create an App for more information on setting up an app in the App Dashboard as well as app types and use cases.
See Authorization for more information on verifying the users and apps that will be accessing the Marketing API and granting them permissions.
- -See Authentication for more information on getting, extending, and renewing access tokens with the Marketing API.
-Beginning June 10, 2025, to improve overall API performance, reach will no longer be returned for standard queries that apply breakdowns and use start_dates more than 13 months old. (Responses to such requests will omit reach and related fields, such as frequency and cpp.)
To apply breakdowns and still retrieve >13-month-old reach values, you can use asynchronous jobs to make up to 10 requests per Ad Account per day. Check the x-Fb-Ads-Insights-Reach-Throttle header to monitor how close you are to that rate-limit, and note that once the rate-limit is breached, requests will omit reach and related fields.
When the rate limit threshold for reach-related breakdowns is exceeded, the following error message will be returned:
-Reach-related metric breakdowns are unavailable due to rate limit threshold.
Facebook Insights API provides performance data from Facebook marketing campaigns. To protect system performance and stability, we have protective measures to equally distribute system resources among applications. All policies we describe below are subject to change.
-The most common issue causing failure for Ads Insights API calls is too many requests and time outs.
- -/GET or synchronous requests can return out-of-memory or timeout errors./POST or asynchronous requests can return timeout errors. For asynchronous requests, it can take up to an hour to complete a request including retry attempts. For example, if you make a query that tries to fetch a large volume of data for many ad level objects.We use data-per-call limits to prevent a query from retrieving too much data beyond what the system can handle. There are 2 types of data limits:
- -These limits apply to both sync and async /insights calls, and we return an error:
error_code = 100, CodeException (error subcode: 1487534)
action_target_id or product_id, and wider date ranges like lifetime./insights edge directly with lower level ad objects to retrieve granular data for that level. For example, first use the account-level query to fetch the list of lower-level object ids with level and filtering parameters. In this example, we fetch all campaigns that recorded some impressions:
-curl -G \
--d 'access_token=<ACCESS_TOKEN>' \
--d 'level=campaign' \
--d 'filtering=[{field:"ad.impressions",operator:"GREATER_THAN",value:0}]' \
-'https://graph.facebook.com/v2.7/act_<ACCOUNT_ID>/insights'
-/<campaign_id>/insights with each returned value to query and batch the insights requests for these campaigns in a single call:
-curl \
--F 'access_token=<ACCESS_TOKEN>' \
--F 'batch=[ \
- { \
- "method": "GET", \
- "relative_url": "v25.0/<CAMPAIGN_ID_1>/insights?fields=impressions,spend,ad_id,adset_id&level=ad" \
- }, \
- { \
- "method": "GET", \
- "relative_url": "v25.0/<CAMPAIGN_ID_2>/insights?fields=impressions,spend,ad_id,adset_id&level=ad" \
- }, \
- { \
- "method": "GET", \
- "relative_url": "v25.0/<CAMPAIGN_ID_3>/insights?fields=impressions,spend,ad_id,adset_id&level=ad" \
- } \
-]' \
-'https://graph.facebook.com'
-filtering parameter only to retrieve insights for ad objects with data. The field value specified in filtering uses DOT notation to denote the fields under the object. Please note that filtering with STARTS_WITH and CONTAIN does not change the summary data. In this case, use the IN operator. See example of a filtering request:curl -G \
--d 'access_token=<ACCESS_TOKEN>' \
--d 'level=ad' \
--d 'filtering=[{field:"ad.impressions",operator:"GREATER_THAN",value:0},]' \
-'https://graph.facebook.com/v25.0/act_<ACCOUNT_ID>/insights'date_preset if possible. Custom date ranges are less efficient to run in our system.Ninety days from the release of v3.3 and effective for all public versions, we change the ad account level rate limit to better reflect the volume of API calls needed. We compute the rate limit quota on your Marketing API access tier and the business owning your app. see Access and Authentication. This change applies to all Ads Insights API endpoints: GET {adaccount_ID}/insights, GET {campaign_ID}/insights, GET {adset_ID}/insights, GET {ad_ID}/insights, POST {adaccount_ID}/insights, POST {campaign_ID}/insights, POST {adset_ID}/insights, POST {ad_ID}/insights.
We use load limits for optimal reporting experience. We measure API calls for their rate as well as the resources they require. We allow a fixed load limit per application per second. When you exceed that limit, your requests fail.
- -Check the x-fb-ads-insights-throttle HTTP header in every API response to know how close your app is to its limit as well as to estimate how heavy a particular query may be. Insights calls are also subject to the default ad account limits shown in the x-ad-account-usage HTTP header. More details can be found here Marketing API, Best Practices
Once an app reaches its limit, the call gets an error response with error_code = 4, CodedException. You should stay well below your limit. If your app reaches its allowed limits, only a certain percentage of requests go through, depending on the query, and the rate.
We apply rate limiting to each app sending synchronous and asynchronous /insights calls combined. The two main parameters limits are counted against are by application, and by ad account.
Here's an example of the HTTP header with an application's accrued score as a percentage of the limits:
-X-FB-Ads-Insights-Throttle: { "app_id_util_pct": 100, "acc_id_util_pct": 10, "ads_api_access_tier": "standard_access" }The header "x-fb-ads-insights-throttle" is a JSON value containing these info:
- -app_id_util_pct — The percentage of allocated capacity for the associated app_id has consumed.acc_id_util_pct — The percentage of allocated capacity for the associated ad account_id has consumed.ads_api_access_tier — Tiers allows your app to access the Marketing API. standard_access enables lower rate limiting.During periods of elevated global load to the /insights endpoint, the system can throttle requests to protect the backend. This can occur in rare cases when too many queries of high complexity (large time ranges, complex metrics, and/or high number of ad object IDs) are coming at the same time. This will manifest in an error that looks like this:
error_code = 4, CodeException (error subcode: 1504022), error_title: Too many API requests
During these periods, it is advised to reduce calls, wait a short period, and query again.
- -/insights queries by pacing them with wait time in your job./insights queries when you come close to hitting 100% utility for your application, or for your ad account.ads_api_access_tier that allows you to access the Marketing API. By default, apps are in the development_access tier and standard_access enables lower rate limiting. To get a higher rate limit and get to the standard tier, you can apply for the "Advanced Access" to the Ads Management Standard Access feature.Fetch stats on many objects and apply filtering and sorting; we made the asynchronous workflow simpler:
- -POST request to <AD_OBJECT>/insights endpoint, which responds with the id of an Ad Report Run.
-{
- "report_run_id": 6023920149050,
-}
-Do not store the report_run_id for long term use, it expires after 30 days.
async_status. Poll this field until async_status is Job Completed and async_percent_completion is 100.
-{
- "id": "6044775548468",
- "account_id": "1010035716096012",
- "time_ref": 1459788928,
- "time_completed": 1459788990,
- "async_status": "Job Completed",
- "async_percent_completion": 100
-}
-Note: Beginning with Marketing API v25.0, if the report fails, you will see the corresponding error_code, error_message, error_subcode, error_user_title, and error_user_msg fields returned by default. See the Ads Insights Error Codes for more details on the returned error codes.
<AD_REPORT_RUN_ID>/insights edge to fetch the final result.
-{
- "data": [
- {
- "impressions": "9708",
- "date_start": "2009-03-28",
- "date_stop": "2016-04-04"
- },
- {
- "impressions": "18841",
- "date_start": "2009-03-28",
- "date_stop": "2016-04-04"
- }
- ],
- "paging": {
- "cursors": {
- "before": "MAZDZD",
- "after": "MQZDZD"
- }
- }
-}
-This job gets all stats for the account and returns an asynchronous job ID:
--curl \ - -F 'level=campaign' \ - -F 'access_token=<ACCESS_TOKEN>' \ - https://graph.facebook.com/v25.0/<CAMPAIGN_ID>/insights -curl -G \ - -d 'access_token=<ACCESS_TOKEN>' \ - https://graph.facebook.com/v25.0/1000002 -curl -G \ - -d 'access_token=<ACCESS_TOKEN>' \ - https://graph.facebook.com/v25.0/1000003/insights -
| Status | Description |
|---|---|
| Job has not started yet. - |
| Job has been started, but is not yet running. - |
| Job has started running. - |
| Job has successfully completed. - |
| Job has failed. Review your query and try again. - |
| Job has expired and skipped. Please resubmit your job and try again. - |
We provide a convenience endpoint for exporting <AD_REPORT_RUN_ID> to a localized human-readable format.
Note: this endpoint is not part of our versioned Graph API and therefore does not conform to its breaking-change policy. Scripts and programs should not rely on the format of the result as it may change unexpectedly.
-- curl -G \ - -d 'report_run_id=<AD_REPORT_RUN_ID>' \ - -d 'name=myreport' \ - -d 'format=xls' \ -'https://www.facebook.com/ads/ads_insights/export_report/' -
| Name | Description |
|---|---|
string | Name of downloaded file - |
enum{csv,xls} | Format of file - |
integer | ID of report to run - |
string | Permissions granted by the logged-in user. Provide this to export reports for another user. - |
Beginning June 10, 2025, to reduce discrepancies with Meta Ads Manager, the use_unified_attribution_setting and action_report_time parameters will be disregarded and API responses will mimic Ads Manager settings:
values will be based on ad set level attribution settings (similar to use_unified_attribution_setting=true), and inline/on-ad actions will be included in 1d_click or 1d_view attribution window data. After this change, standalone inline attribution window data will no longer be returned.action_report_time=mixed: on-Meta actions (e.g., Link Clicks) will use impression-based reporting time; whereas off-Meta actions (e.g., Web Purchases) will leverage conversion-based reporting time.The default behavior of the API is different from the default behavior of Ads Manager. If you would like to observe the same behavior as in Ads Manager, set the use_unified_attribution_setting field to true.
Make programatic, automated actions on ad objects or Pages, or do programmatic ads buying. System users represent servers or software making API calls to assets owned or managed by a Business Manager.
-The easiest, quickest way to create a system user is in the Business Manager tool. See Ads Help Center: How do I add a new System User.
-To take actions for a person managing an ad account, you should take them through the standard Facebook oAuth flow and get a user access token. If you try to use system user tokens to work on ad objects or Pages on behalf of a real user of your software, you cannot link this user to those actions unless you take them through Facebook Login.
-Overview- -Learn about the two different types of system users: admin system user and system user. Read API limits for system users. - -Guide: Create System Users- -Create, retrieve, and update a system user. Learn how to invalidate access tokens. - -Guide: Install Apps and Generate Tokens- -A guide to install apps and generate access tokens through your system user. - | Guide: Permissions- -Assign system user tasks on ad accounts, user page, and proxied assets. Retrieve permissions. - -Guide: API Calls- -How to make automated API calls for Marketing API and Pages API. - -System Users And Custom Audiences- -Rules for system users to operate with a Custom File Custom Audience. - |