<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Build Things That Matter]]></title><description><![CDATA[Build Things That Matter]]></description><link>https://420morganjason.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Build Things That Matter</title><link>https://420morganjason.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 19 Sep 2026 03:43:57 GMT</lastBuildDate><atom:link href="https://420morganjason.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[THE COMPLETE FIELD GUIDE TO PASSING META APP REVIEW ]]></title><description><![CDATA[There is a moment, somewhere around the third rejection, where you start to suspect the Meta App Review is not a technical process at all.
You're right. It isn't.
We spent three months building the Me]]></description><link>https://420morganjason.hashnode.dev/the-complete-field-guide-to-passing-meta-app-review</link><guid isPermaLink="true">https://420morganjason.hashnode.dev/the-complete-field-guide-to-passing-meta-app-review</guid><category><![CDATA[Metappareview]]></category><category><![CDATA[AppReview]]></category><category><![CDATA[Meta]]></category><category><![CDATA[#softwareengineer ]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[Morgan Jason]]></dc:creator><pubDate>Sun, 06 Sep 2026 13:11:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8b2c0b85d6404e33d21d60/c63aae14-7ccf-4896-88ae-1a3982cb8056.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There is a moment, somewhere around the third rejection, where you start to suspect the Meta App Review is not a technical process at all.</p>
<p>You're right. It isn't.</p>
<p>We spent three months building the Meta integration for <strong>MarketPilot AI,</strong> a platform that helps digital marketers plan, publish, and measure campaigns from one place. The engineering was the easy part. The hard part was proving to a stranger, in a video, that we deserved access to other people's data.</p>
<p>This guide is what we wish had existed when we started. It's long because App Review is long. Read it once end to end, then keep it open in a tab while you work. Everything here is ordered the way you should actually do it.</p>
<p><strong>What you'll find inside:</strong> the vocabulary, a five-week build order, the Instagram Login vs Facebook Login decision that trips up half of all applicants, a copy-paste submission notes template, a shot-by-shot screencast script, a rejection-to-fix lookup table, and the post-approval obligations nobody warns you about.</p>
<h3><strong>The one mental model that changes everything</strong></h3>
<p>Before any of the mechanics, internalise this:</p>
<blockquote>
<p>"<em>You are not being tested . You are being understood, by a person under time pressure, using only the video you gave them."</em></p>
</blockquote>
<p>Reviews work through a queue. They are not going to explore your product, guess at your architecture, or give you the benefit of the doubt. They open your submission, read your notes, watch your screencast, and decide whether they can see each requested permission being used for the reason you claimed.</p>
<p>Three consequences fall out of this, and they explain almost every rejection ever issued:</p>
<ol>
<li><p><strong>If it isn't in the video, it doesn't exist.</strong> Your beautiful feature, your clever architecture, your enterprise clients: invisible. The video is the entire submission.</p>
</li>
<li><p><strong>Ambiguity reads as risk.</strong> A reviewer who isn't sure what they're looking at will reject .It costs them nothing to reject. It costs them a lot to wrongly approve.</p>
</li>
<li><p><strong>Each permission is judged separately.</strong> You can be approved for six scopes and denied two in the same submission, with different reasons for each. Your product isn't approved or rejected. Your <em>permissions</em> are.</p>
</li>
</ol>
<p>Everything below is in service of making a tired person's job easy.</p>
<p><strong>The vocabulary, in plain English</strong><br />You cannot navigate the dashboard until these five words mean something concrete to you.</p>
<table>
<thead>
<tr>
<th>Term</th>
<th>What it actually means</th>
</tr>
</thead>
<tbody><tr>
<td>Permissions(Scope)</td>
<td>A specific key, like "read this Page's posts." Your app requests them during login. Named things like <strong>pages_read_engagement</strong></td>
</tr>
<tr>
<td>Feature</td>
<td>A capability rather than a data key, like Human Agent messaging or Instagram Public Content Access. Reviewed the same way</td>
</tr>
<tr>
<td>Use Case</td>
<td>A bundle of permissions and features describing a job your app does, such as "Manage everything on your Page." Since 2024, this is how you apply.</td>
</tr>
<tr>
<td>Standard Access</td>
<td>The default. Works only for people who have a role on your app, and business assets you own. Fine for building. Useless for customers.</td>
</tr>
<tr>
<td>Advanced Access</td>
<td>What App Review grants. Lets your app use a permission with anyone, including customers who've never heard of you. This is the prize.</td>
</tr>
</tbody></table>
<p>And two modes:</p>
<ul>
<li><p><strong>Development mode</strong> - your app works fully, but only for admins, developers and testers.</p>
</li>
<li><p><strong>Live mode</strong> - your app works for the public, but only for permissions granted Advanced Access.</p>
</li>
</ul>
<p><strong>The trap most people fall into:</strong> they build the whole product in Development mode where everything works beautifully, then flip to Live and watch it collapse, because Standard Access silently returns empty arrays instead of errors. Empty <em>data: []</em> is not a bug. It's Meta telling you that you don't have Advanced Access yet.</p>
<p><strong>Why MarketPilot needed this at all</strong><br />Our integrations section connects a marketer's Facebook Pages, Instagram business account, an ad account, then pulls performance data into one dashboard and pushes scheduled content back out. Every single one of those actions touches data belonging to someone who is not us. There is no version of that product that works on Standard Access. App Review<br />wasn't a compliance checkbox for us. It was the gate between a demo and<br />a business.<br />If your product does anything on behalf of a client, you're in the same position. Plan for it as a workstream not a form.</p>
<p><strong>The build order (roughly five weeks)</strong><br />Do these in sequence. The single most expensive mistake in App Review is doing things in the wrong order and discovering a blocker after a two-week wait.</p>
<p>Week 1: Business Verification and app Hygiene<br /><strong>Start Business Verification on day one:</strong> It runs in a completely separate queue from App Review, it gates Advanced Access on the more sensitive permissions, and it's the one thing you cannot rush at the end.</p>
<p>You'll need documents where the business name and address match exactly across all of the. Typically a certificate of incorporation or business registration, plus a proof of address such as a utility bill or bank statement. A mismatch between "MarketPilot Ltd" and "MarketPilot Limited" on two documents in enough to fail. Check before uploading.</p>
<p>You'll also be asked to classify your business. If your app provides services to other businesses who use it to manage their own Meta assets(which is what MarketPilot does, and probably what you do), you're a <strong>Tech Provider.</strong> Getting this wrong changes the permissions you're eligible for, so read the definitions carefully rather than guessing.</p>
<p>While verification is in the queue, fix your app settings. Reviewers click every one of these:</p>
<ul>
<li><p><strong>Privacy Policy URL</strong> - same</p>
</li>
<li><p><strong>Terms of Service URL</strong> - same</p>
</li>
<li><p><strong>App Icon</strong>(1024x1024) and a real category</p>
</li>
<li><p><strong>Data Deletion Request Callback</strong> - a live endpoint, not a placeholder</p>
</li>
<li><p><strong>Deauthorize callback</strong> - fires when a user removes your app</p>
</li>
<li><p><strong>Valid OAuth redirects URIs</strong> and your app domains</p>
</li>
</ul>
<p>A 404 on any of these can sink an otherwise flawless submission. This is the cheapest possible thing to get right, and it fails people instantly.<br />For your engineer: the two callbacks are POST endpoints receiving a signed request from Meta. Verify the signature with your app secret, then delete<br />or queue deletion of that user's data. The deletion endpoint must return a JSON body containing a confirmation URL and code that a user could check</p>
<p><strong>Week 2: Pick your login architecture</strong><br />This is the fork in the road, and choosing wrong means rebuilding your auth flow after a rejection.<br />If you're touching Instagram, there are <strong>two entirely different</strong> ways to connect, with different permissions, different capabilities, and different review expectations.</p>
<table>
<thead>
<tr>
<th></th>
<th>Instagram API with Facebook Login</th>
<th>Instagram API with Instagram Login</th>
</tr>
</thead>
<tbody><tr>
<td>User Logs in with</td>
<td>Their Facebook account</td>
<td>Their Instagram account directly</td>
</tr>
<tr>
<td>Requires the IG account linked to a Facebook Page</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Typical Scopes</td>
<td><code>instagram_basic</code>, <code>instagram_manage_insights</code>, <code>instagram_manage_comments</code>, <code>instagram_content_publish</code>, plus <code>pages_show_list</code>, <code>pages_read_engagement</code></td>
<td><code>instagram_business_basic</code>, <code>instagram_business_manage_insights</code>, <code>instagram_business_manage_comments</code>, <code>instagram_business_content_publish</code></td>
</tr>
<tr>
<td>Best for</td>
<td>Tools that also need Pages and Ads data</td>
<td>Instagram-only tools</td>
</tr>
<tr>
<td>Extra reach</td>
<td>Can access Page data and ad accounts in the same flow</td>
<td>Simpler onboarding, fewer moving parts</td>
</tr>
</tbody></table>
<p>Choose based on your product, not on which tutorial you found first. If, like us, you need Pages, Instagram, and ads in a single connect flow, Facebook Login us the right answer and the extra permission surface is worth it. If you only publish to Instagram, Instagram Login gets your users through onboarding with far less friction.</p>
<p>Then design your token flow, because this is where production breaks.</p>
<ol>
<li><p>The login returns a short-lived user token(about an hour).</p>
</li>
<li><p>Exchange it server-side for a long-lived user token(roughly 60 days).</p>
</li>
<li><p>Use that to fetch <strong>Page access tokens,</strong> which don't expire on their own as long as the underlying long-lived token stays valid and the user doesn't revoke or change their password.</p>
</li>
<li><p>Store tokens encrypted. Build a refresh job. Build a graceful "please reconnect" state in your UI.</p>
</li>
</ol>
<p>Nothing about token expiry is part of App Review, but a reviewer clicking around a broken integration because your token died overnight absolutely is.</p>
<p>Week 3: Build, and generate real API calls</p>
<p>Now open Use Cases in the dashboard. You'll see a catalogue: <em>Create &amp; manage ads with Marketing API, Measure ad performance data, Manage messaging &amp; content on Instagram, Manage everything on your Page, Access the Threads API, Connect with customers through WhatsApp, Manage products with Catalog API,</em> and more.</p>
<p>Pick the ones that describe your product and hit <strong>Customize</strong>. That screen is where the real work lives.</p>
<p><strong>The Permissions and features table has four columns that matter:</strong><br /><strong>The permission,</strong> with <em>Full Description</em> and <em>Requirements</em> links beneath it. The Requirements link tells you, per permission, exactly what your screencast must demonstrate. Read every single one before you plan your recording. A shocking share of rejections are just people who never opened this.</p>
<p>API Calls. Meta counts how many times your app has actually hit the endpoints behind that permission. A scope showing a dash is a scope you haven't proven you use. <strong>You need real, logged calls before you submit.</strong> This is not optional and it catches people by surprise the day they try to submit.</p>
<p><strong>Status.</strong> Scopes you hold show <em>Ready to publish</em>. Everything else shows a dash.<br /><strong>Action.</strong> Either a dropdown for permissions you already have, or an <strong>Add to App Review</strong> button for ones you want. That button is how a permission enters your submission.</p>
<p><strong>Request only what you use.</strong> Every extra permission is another thing you must demonstrate on camera and another chance to be denied. Nine well-demonstrated scopes beat twelve half-shown ones, and a denial on any one of them costs you two weeks.</p>
<p><strong>Week 4: Testers, and a working demo</strong><br />Under <strong>App Roles,</strong> add Administrators, Developers and <strong>Testers</strong>.</p>
<p>This is the most underused feature in the entire dashboard. <strong>Anyone with a role on your app can grant your app unapproved permissions on their own account.</strong> That means a fully working integration, with real data, before Meta has approved anything.</p>
<p>Use it to:</p>
<ul>
<li><p>Give engineers real Pages and real ad accounts to build against.</p>
</li>
<li><p>Let a friendly design partner trial the integration early and tell you what's confusing.</p>
</li>
<li><p>Record a screencast in which every button genuinely works.</p>
</li>
</ul>
<p>The catch: testers must accept the invite from their own Facebook settings under Apps and Websites. Half of all "it's not working" bug reports are an unaccepted invite.</p>
<p>Meanwhile, the <strong>Testing</strong> section creates a <strong>test app</strong> (a clone with its own App ID)and <strong>test users</strong> with synthetic Pages and data. Use it for experiments you don't want anywhere near production config. Do not record your final screencast on a test app, though. Reviewers want your real product at your real domain</p>
<p><strong>Week 5: Record, write, submit</strong><br />That's the next two sections.</p>
<p><strong>The submission notes</strong><br />Your written notes are the second thing the reviewer reads, right after your app name. Most people write two vague sentences here. Don't. The notes are where you tell the reviewer what they're about to see, so that when they see it, they recognise it.</p>
<p><code>APP OVERVIEW MarketPilot AI is a marketing platform for agencies and in-house marketing teams. Users connect the Facebook Pages, Instagram business accounts, and ad accounts they manage, and MarketPilot shows unified performance reporting and schedules content across those channels.</code></p>
<p><code>TEST CREDENTIALS URL: https://app.marketpilot.io Email: reviewer@marketpilot.io Password: [password] These credentials were verified working on [date].</code></p>
<p><code>HOW TO REPRODUCE (also shown in the screencast)</code></p>
<ol>
<li><p><code>Go to https://app.marketpilot.io and sign in with the credentials above.</code></p>
</li>
<li><p><code>Click "Integrations" in the left sidebar.</code></p>
</li>
<li><p><code>Click "Connect Facebook". The Meta login dialog opens.</code></p>
</li>
<li><p><code>Grant access to the Page "MarketPilot Demo" and the linked Instagram business account.</code></p>
</li>
<li><p><code>You land on the Integrations page showing the connected assets.</code></p>
</li>
<li><p><code>Click "Analytics" to see Page and Instagram performance data.</code></p>
</li>
<li><p><code>Click "Content &gt; Schedule" to publish a post.</code></p>
</li>
</ol>
<p><code>PERMISSIONS</code></p>
<p><code>pages_show_list Why we need it: to display the list of Pages a user manages so they can choose which ones MarketPilot should work with. Where it appears in the app: the Page selector on the Integrations page. In the screencast: 00:42 - 00:58.</code></p>
<p><code>pages_read_engagement Why we need it: to read post and engagement data for the connected Page so we can show which content performed best. Where it appears: the Analytics dashboard, "Top Posts" module. In the screencast: 01:20 - 01:55.</code></p>
<p><code>instagram_manage_insights Why we need it: to retrieve reach and engagement metrics for the connected Instagram business account and display them alongside Facebook data in one report. Where it appears: the Analytics dashboard, Instagram tab. In the screencast: 02:10 - 02:48.</code></p>
<p><code>ARCHITECTURE NOTE [Include only if true] Parts of our data sync run server-to-server using a system user token, so no front-end login flow is visible for those calls. The user-facing login and consent flow is shown in full at the start of the screencast.</code></p>
<p>Three things this template does that a paragraph cannot:</p>
<ul>
<li><p><strong>Timestamps</strong> turn your video into a document the reviewer can navigate. This alone speeds up approvals.</p>
</li>
<li><p><strong>"Where it appears in the app"</strong> answers the exact question the reviewer is asking.</p>
</li>
<li><p><strong>The architecture note</strong> pre-empts the "we could not see the login flow" rejection, which is otherwise almost impossible to escape for backend-heavy apps.</p>
</li>
</ul>
<p><strong>The screencast: a director's guide</strong><br />This is the whole ballgame. Budget half a day for it and treat it like production, not a screen grab.</p>
<p><strong>Pre-production checklist</strong></p>
<ul>
<li><p>Test credentials verified in an incognito window, on a second device, <strong>that morning</strong></p>
</li>
<li><p>Two-factor authentication turned off on the demo account, or the reviewer cannot log in</p>
</li>
<li><p>Demo Page and Instagram account populated with <strong>real-looking content</strong>: posts, comments, some engagement history. Empty dashboards read as a fake product.</p>
</li>
<li><p>Every integration working end to end when logged in as the test user</p>
</li>
<li><p>App interface language set to <strong>English</strong></p>
</li>
<li><p>Browser cleaned up: no personal bookmarks, no unrelated tabs, no notifications</p>
</li>
<li><p>Recording at 1280×720 or better, in a maximised window</p>
</li>
<li><p>Cursor visible and moving deliberately</p>
</li>
</ul>
<p><strong>The script</strong><br />Record <strong>one continuous take per use case.</strong> Narrate out loud, or add clear captions if you'd rather not speak. Its not a necessity either. You can as well record only.<br />-Show the browser address bar with your real domain. Not localhost. Not a staging subdomain that looks like a test. Then log in on camera with the exact credentials you gave Meta.<br />-Don't teleport via a deep link. Show the navigation. The reviewer needs to believe a real customer could find this feature.<br />-Show the login dialog, scroll the permission screen so the scopes are <strong>readable on camera,</strong> show the Page and asset selector, show the click that grants consent. <strong>Do not cut anywhere in this section.</strong> A jump cut around consent looks like something is being hidden, and it will be read that way.</p>
<p><strong>Prove every single permission</strong></p>
<p>This is where approvals are won. For each scope you requested, show the <em>result</em> on screen. If you're narrating, say the permission's name out loud as you show it. If you're not, put it on screen as a caption.</p>
<table>
<thead>
<tr>
<th>Permission</th>
<th>What must be visible on camera</th>
</tr>
</thead>
<tbody><tr>
<td>pages_show_list</td>
<td>The user's real Pages appearing in your UI and then selecting one</td>
</tr>
<tr>
<td>pages_read_engagement</td>
<td>Actual post and engagement data rendered in your dashboard</td>
</tr>
<tr>
<td>read_insights</td>
<td>Page-level metrics with real numbers, not zeroes</td>
</tr>
<tr>
<td>pages_manage_posts</td>
<td>You composing a post, publishing, then the post visible on the actual Page</td>
</tr>
<tr>
<td>instagram_basic</td>
<td>The connected IG account and its media loaded in your UI</td>
</tr>
<tr>
<td>instagram_manage_insights</td>
<td>Real Instagram reach or engagement figures in your reporting view</td>
</tr>
<tr>
<td>instagram_manage_comments</td>
<td>Comments loading, and you replying to or moderating one</td>
</tr>
<tr>
<td>instagram_content_publish</td>
<td>The publish action, then the post live on Instagram</td>
</tr>
<tr>
<td>ads_read</td>
<td>Campaign spend and performance loading into your reporting</td>
</tr>
<tr>
<td>ads_management</td>
<td>A campaign being created, edited, or paused from your interface</td>
</tr>
</tbody></table>
<p>Loading spinners don't count. Empty states don't count. Placeholder data does not count and is actively suspicious. <strong>Real data on screen, tied to the account you just connected counts,</strong></p>
<p><strong>Closing - the disconnect flow</strong></p>
<blockquote>
<p>"Finally, a user can remove the connection at any time. I click Disconnect, confirm, and MarketPilot no longer has access. The data is removed from the dashboard."</p>
</blockquote>
<p>Almost nobody records this. It takes fifteen seconds and it tells the reviewer you treat consent as reversible. It is one of the highest-return shots in the whole video.</p>
<p><strong>The twelve things that get screencasts rejected</strong></p>
<ol>
<li><p>The permission was never actually shown being used.</p>
</li>
<li><p>The login flow was cut, sped up, or skipped.</p>
</li>
<li><p>Test credentials didn't work, or 2FA blocked the reviewer.</p>
</li>
<li><p>The app interface was not in English.</p>
</li>
<li><p>No narration and no captions, leaving the reviewer to guess.(Not necessary)</p>
</li>
<li><p>Recorded on localhost or an obvious prototype</p>
</li>
<li><p>A phone camera pointed at a monitor, or a slideshow instead of a screenrecording</p>
</li>
<li><p>Empty dashboards with no real data</p>
</li>
<li><p>The video shows a different flow than the written instructions described.</p>
</li>
<li><p>Only some of the requested permissions were demonstrated</p>
</li>
<li><p>Server-to-server architecture not disclosed , so the missing login flow looked like an omission.</p>
</li>
<li><p>Privacy policy, terms, or data deletion URLs returning errors when clicked</p>
</li>
</ol>
<p>Upload the video file directly to the submission where you can. A reviewer who hits a permission wall on your video link will reject rather than email you.</p>
<p><strong>How to read a rejection</strong><br />Here is the thing nobody tells first-timers, and it's genuinely good news:</p>
<p><strong>Meta tells you the specific reason for every individual rejected scope.</strong></p>
<p>Open your App Review submission feedback and you'll see each denied permission listed by name, on it's own card, containing a short reason title. the developer policy it falls under, a numbered list of exactly what your next screencast must contain , links to the screen recording guide and the common rejection guidelines, and a <strong>Request again</strong> button.</p>
<p>That numbered list is a grading rubric handed to you after the exam. In our case, nearly every rejection reduced to the same four asks: show the complete login flow, show the user granting access, show the end-to-end experience of the use case, and follow the recording best practices around English UI.</p>
<p>So when denial lands, resist the urge to rewrite everything. Find the one specific thing they couldn't see, fix that and resubmit.  </p>
<p><strong>Rejection -&gt; root cause -&gt; fix</strong></p>
<table>
<thead>
<tr>
<th>What the feedback says</th>
<th>What actually happened</th>
<th>What to do</th>
</tr>
</thead>
<tbody><tr>
<td>Screencast doesn't demonstrate the end-to-end experience</td>
<td>You showed the connection but never the payoff</td>
<td>Re-record showing real data from that scope on screen</td>
</tr>
<tr>
<td>Could not complete the login flow</td>
<td>Broken credentials, 2FA, or you cut the consent screen</td>
<td>Fresh credentials, 2FA off, uncut consent clip</td>
</tr>
<tr>
<td>Permission not justified by the use case</td>
<td>You asked for write access but only demonstrated reading</td>
<td>Demo the write action, or drop the scope</td>
</tr>
<tr>
<td>Insufficient detail in submission notes</td>
<td>Vague instructions that didn't match the video</td>
<td>Use the notes template with timestamps</td>
</tr>
<tr>
<td>App does not appear to be functional</td>
<td>Empty states, placeholder data, or errors on camera</td>
<td>Populate the demo account properly, re-record</td>
</tr>
<tr>
<td>Business verification incomplete</td>
<td>Documents mismatched or still in queue</td>
<td>Fix documents, wait for that separate queue</td>
</tr>
<tr>
<td>Login flow not visible</td>
<td>You're server-to-server and didn't say so</td>
<td>State it explicitly in the notes and resubmit</td>
</tr>
</tbody></table>
<p><strong>Resubmission Strategy</strong></p>
<ul>
<li><p><strong>Submit all permissions for a use case together.</strong> Sequential submissions mean sequential two-week waits.</p>
</li>
<li><p><strong>Expect 10 to 14 business days for a decision.</strong> Some come back in three days. Some sit for two weeks and change. Business Verification runs on its own clock entirely.</p>
</li>
<li><p><strong>Keep building during the wait.</strong> Testers give you a working integration, so nothing needs to be blocked.</p>
</li>
<li><p><strong>Don't spam re-submissions.</strong> Repeated low-effort attempts slow your queue position and win you no goodwill.</p>
</li>
<li><p><strong>Change something real between attempts.</strong> Resubmitting the same video with a longer paragraph is how people lose two months.</p>
</li>
</ul>
<p>Ours took multiple rounds. A lot of back and forth. Re-recording the same flow at 1am because a reviewer couldn't clearly see the Page selector. It is tedious, and then one morning the email says <em>Approved</em> and you forget it all.</p>
<p><strong>What happens after approval</strong></p>
<p>Approval is a beginning, not an ending. Three things will surprise you.<br /><strong>Advanced Access can be revoked.</strong> If your app violates platform terms,sits unused for a long period, or fails a later assessment, permissions can be pulled. Approval is a licence, not a deed.</p>
<p><strong>You will likely face a Data Protection Assessment</strong>. Apps with advanced access to certain data are periodically required to complete a detailed questionnaire about how they store , share, and protect user data, including your security practices and any third parties you send data to. It arrives with a deadline, and missing the deadline costs you your access. Answer it seriously, with your actual engineering practices in hand.</p>
<p><strong>Rate limits become your new problem.</strong> Platform limits scale with your user count, and the Marketing API applies its own business use case limits per ad account. Build caching, batching, and exponential backoff before your first big customer, not after they complain that reports are empty.</p>
<p>Also plan for the mundane: tokens expiring, users changing passwords, clients removing your app, and Meta deprecating API versions on a rolling schedule. Every one of these looks identical to your customer, who sees only "my data is gone." Build a clear reconnect flow and a status indicator on your integrations page. Your support inbox will thank you.</p>
<p><strong>The last thing</strong><br />Meta App Review isn't a measure of your engineering. It's a measure of how clearly you can show a stranger, in one unbroken take, that you're using someone's data for exactly the reason you said you would.<br />Build the feature properly. Then film it honestly. Everything else is Admin.</p>
<p><em>We're building MarketPilot AI for digital marketers who are tired of stitching six tools together. If you're stuck in App Review right now, drop your rejection reason in the comments and I'll tell you what we did about the exact one.</em><br /><em>API versions, permission names, and dashboard layouts change. Verify anything version-specific against Meta's current documentation before you build it.</em></p>
]]></content:encoded></item><item><title><![CDATA[I needed a Google API: Here's how I applied and got approved.]]></title><description><![CDATA[There is a particular kind of frustration that only comes from building something for months. You fix one bug, another appears. You fix that one. A tester in another country finds something you never ]]></description><link>https://420morganjason.hashnode.dev/i-needed-a-google-api-here-s-how-i-applied-and-got-approved</link><guid isPermaLink="true">https://420morganjason.hashnode.dev/i-needed-a-google-api-here-s-how-i-applied-and-got-approved</guid><category><![CDATA[Googleapi]]></category><category><![CDATA[googlereview]]></category><dc:creator><![CDATA[Morgan Jason]]></dc:creator><pubDate>Sat, 29 Aug 2026 21:58:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8b2c0b85d6404e33d21d60/bf38b7f9-3ddc-4923-b175-a7e0287a14e9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There is a particular kind of frustration that only comes from building something for months. You fix one bug, another appears. You fix that one. A tester in another country finds something you never considered. You fix it. Then another tester says, <em>"What happens if I do this?"</em> And suddenly you're debugging something at 2 AM that you didn't even know it existed. That has been the last three months of building <a href="https://marketpilot.io/"><strong>MarketPilot</strong></a>. <strong>MarketPilot</strong> AI is a tool designed to replace six disconnected tools. Here, you can Plan content, Publish across different social platforms, Run ads, answer your community, work with creators and ship client ready reports all from one AI-powered workspace. But building the application was only part of the journey. Eventually, we reached another wall:</p>
<p><strong>Google</strong>.<br />And this time, I couldn't simply write code and move on. I had to explain what my application does, what Google data it needs, and why it needs that data, demonstrate the functionality, record a video, submit everything for review - and then wait. The article is about that process.</p>
<p><strong>Why do you need Google APIs?</strong><br />If you're new to Google Cloud, the words Google APIs, OAuth, scopes and verification can sound unnecessarily complicated. They become much easier when you think about them this way:</p>
<p>"Your application wants to talk to Google's services on behalf of a user. Google wants to know exactly what your application is going to do."</p>
<p>For <strong>MarketPilot,</strong> integrations such as Google Ads, Google Analytics 4, YouTube and Google Tag Manager, Google Merchant Center aren't just nice-to-have features. They allow MarketPilot to connect marketing activity with the rest of customer's digital ecosystem.<br />For example:</p>
<p><strong>YouTube API</strong> - allows the application to work with a user's YouTube account and related resources.<br /><strong>Google Ads API -</strong> allows the application to work with advertising accounts and campaign data<br /><strong>Google Analytics 4 API</strong> - allows the application to retrieve analytics data so marketing performance can be understood.<br /><strong>Google Tag Manager API -</strong> allows the application to interact with tag management resources.</p>
<p>The exact permissions depend on what your application actually needs. And the last sentence is important . <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">ASK FOR THE MINIMUM PERMISSIONS YOU NEED. </mark></strong> Don't request every scope simply because it is available. Google explicitly recommends using less sensitive scopes where possible and sensitive scopes can require additional review</p>
<p><strong>STEP 1: CREATE A GOOGLE CLOUD PROJECT</strong><br />First, go to <strong>Google Cloud Console</strong> and create a new project for your application. Give it a name that clearly identifies your app, such as <strong>MarketPilot AI</strong>. This project will be where you manage your APIs, OAuth settings, credentials and other Google Cloud services. Once your project exists, you'll spend a lot of time inside <strong>APIs &amp; Services</strong><mark class="bg-yellow-200 dark:bg-yellow-500/30"><br /></mark><br /><strong>STEP 2: FIND APIs YOU NEED</strong><br />Once your project is created , go to APIs &amp; Services -&gt; Library. Search for the Google APIs your application needs, such as YouTube, Google Ads, Google Analytics, or Google Tag Manager. Open each API and click <strong>Enable</strong>. Before enabling anything, write down: "<em>What feature in my application requires this API?"</em> Only enable the APIs your application actually uses. That simple exercise makes the next step - OAuth scopes - much easier.</p>
<p><strong>STEP 3: CONFIGURE OAUTH</strong><br />Next, go to your OAuth configuration under <strong>Google Auth Platform/ OAuth consent configuration</strong>. This is where you tell Google about your application - it's name, logo, website, contact details, and how users will sign in and give permission to your app**.** Think of the OAuth consent screen as the conversation between your application and the user:</p>
<p>"<strong>MarketPilot wants access to X because it needs it to do Y"</strong></p>
<p>The user should understand what they're granting.</p>
<p><strong>STEP 4: BE VERY CAREFUL WITH SCOPES</strong><br />Scopes define what your application is allowed to access. Add only the scopes your application genuinely needs. For example, if MarketPilot needs to read a user's analytics data, request the appropriate Analytics scope. Avoid adding extra permissions just because they are available. The temptation is to add every scope you think you might need someday,Don't. Instead, go through your application feature by feature.</p>
<p><strong>A useful rule<br />Feature = Required Google data = Required Scope</strong></p>
<p>Not:</p>
<p><strong>Google API = Every Scope available</strong><br />That distinction can save you a lot of trouble later.</p>
<p><strong>STEP 5: REMOVE SCOPES YOU DON'T NEED</strong><br />This sounds obvious, but it's surprisingly easy to forget. During development, you may experiment with different APIs and permissions. Eventually, you should clean them up.<br />If a scope is no longer required, remove it from your configured data access <strong>and remove it from your application'S OAuth requests.</strong><br />Google specifically warns that removing a scope from the console but continuing to request it in your application can cause problems, including an unverified-app warning.<br />Before submitting your application, I recommend creating a simple checklist:<br />-API enabled<br />-OAuth client configured<br />-Required scopes added<br />-Unnecessary scopes removed<br />-Application requests exactly those scopes<br />-Privacy Policy available<br />-Terms/details completed where required<br />-Application is working in production<br />-Demo account/test flow prepared</p>
<p><strong>STEP 6: EXPLAIN WHY YOU NEED EACH SCOPE</strong><br />For each sensitive scope, explain why your application needs it and how it will be used. Keep the explanation simple and specific. Tell the reviewer what the user does, what data is accessed, and what your application does with that data. Explain the user journey. The reviewer should be able to answer three questions immediately:<br /><strong>What data are you accessing?<br />Why do you need it?<br />Where is it used in the applications?</strong></p>
<p><strong>For example:</strong><br />"A MarketPilot user connects their Google Analytics property so the application can retrieve performance and metrics and display them alongside their marketing activity. The data is used to generate performance reports and recommendations inside the user's MarketPilot workspace,"</p>
<p><strong>STEP 7: RECORD THE DEMO VIDEO</strong><br />This was probably the part I paid the most attention to.<br />Google doesn't just want a marketing video. They want to see the <strong>actual application and OAuth flow.</strong> Your demo should show the end-to-end experience. Google's current guidance says the video should demonstrate the OAuth grant process, the complete consent scree, the requested scopes, and the functionality that uses those scopes.<br />For MarketPilot, I approached the video almost like a product walk-through for a reviewer.</p>
<p><strong>My demo flow</strong><br /><strong>On an incognito tab</strong><br /><strong>1. Show MarketPilot</strong><br />Start with the actual application<br />Show the application name and branding.</p>
<p><strong>2. Show the feature requiring Google Access</strong><br />Don't make the reviewer guess why you're asking for permission<br />Show the actual feature.</p>
<p><strong>3. Start the connection process</strong><br />Click the Google connection button</p>
<p><strong>4. Show the OAuth consent screen</strong><br />Let the reviewer see what permissions are being requested.</p>
<p><strong>5. Authorize the application</strong><br />Complete the OAuth flow</p>
<p><strong>6. Return to MarketPilot</strong><br />Show that Google account is now connected</p>
<p><strong>7. Demonstrate the functionality</strong><br />This is critical. Don't stop after authentication. Show what your application actually does with that authorized data.</p>
<p><strong>For example:</strong><br />Connect Google Analytics -&gt; Retrieve data -&gt; Display it in MarketPilot -&gt;<br />Use that data in reporting</p>
<p>That closes the loop.</p>
<p><strong>STEP 8: UPLOAD THE DEMO</strong><br />Google's current guidance for verification asks for a demonstration video, and for restricted-scope verification the documentation specifically recommends uploading the video to YouTube with visibility set to <strong>Unlisted.</strong></p>
<p>In case you fail any of the scopes in the screencast, the Google verification team will respond to you via email with instructions on what you need to do next. You can simply follow the steps they provide.<br />Since the video I initially uploaded to YouTube kept getting removed, I decided to host it on Google Drive instead. I set the sharing permissions to "Anyone with the link - Viewer" and then shared the Google Drive link via email</p>
<p>I kept the video focused.<br />No dramatic intro.<br />No 10-minute explanation of the startup.<br />No background music.<br />The reviewer doesn't need commercial.<br />They need evidence.</p>
<p>Show the product. Show the OAuth flow. Show the scopes. Show how the permissions are actually used.<br />If you use multiple OAuth clients or multiple OAuth workflows, Google expects the relevant flows to be demonstrated as well.</p>
<p><strong>STEP 9: SUBMIT THE APPLICATION</strong><br />Once everything is ready, the final stage is submission.<br />Google's current process requires the application to be published to production before preparing it for verification. You then review the configured information, provide scope justification, provide the demo video, and submit the verification request.</p>
<p><strong>Useful Google Documentation</strong><br />If you're going through this process yourself, these are the pages I would bookmark:<br /><a href="https://docs.cloud.google.com/apis/docs/getting-started?utm_source=chatgpt.com">Google Cloud - Getting Started with Cloud APIs</a><br /><a href="https://developers.google.com/identity/protocols/oauth2/scopes?utm_source=chatgpt.com">Google - OAuth 2.0 Scopes</a><br /><a href="https://support.google.com/cloud/answer/13461325?utm_source=chatgpt.com">Google Cloud - Submitting Your App for Verification</a><br /><a href="https://support.google.com/cloud/answer/13804565?utm_source=chatgpt.com">Google Cloud - Demo Video Requirements</a></p>
<p>If you're building something that needs Google APIs and you're currently staring at the Cloud Console wondering where to start - I hope this saves you some time.</p>
]]></content:encoded></item></channel></rss>