Michiu / marketing reference

External approvals — action checklist

Action-oriented checklist for the Meta + LinkedIn + WhatsApp approvals that gate live publishing in the social and dm-inbox lanes. Engineering reference docs in this directory describe API surfaces; this file describes the human dashboard work that has to land before that code can call those APIs.

Status as of 2026-05-05: none started. All four tracks below need to be kicked off. Total elapsed time from "submit everything" to "everything approved" is typically ~2-3 weeks.

Update the checkboxes as you progress so a future session can see where things stand.


Why this file exists

Q3 social publishing (PR #28/#29/#30, all merged) and DM-inbox slice 3 (Messenger webhook, not started) are gated on Meta + LinkedIn approvals. WhatsApp Business replaces the deferred Slack SLA-alert path per #39. The code is or will be ready before the access tokens are — the bottleneck is approval cycles, not implementation.

Each track lists the exact dashboard steps, what to write in the use-case forms, what tokens to capture, and what env vars they map to in this repo's .env.example.


Order of operations recommendation

  1. Today: Start Business Verification (prereq below). Apply for LinkedIn Marketing Developer Platform (Track 3) — independent of Meta, parallel.
  2. Day 1-7 while waiting on Business Verification: Write privacy + terms pages on the public michiu.nl site if missing (Meta requires public URLs — Cloudflare-Access-gated docs will fail review). Record screencasts for Tracks 1 + 2 against mock-mode.
  3. Once Business Verification clears: Submit all five Meta permissions (Track 1 + 2). Start WhatsApp number provisioning (Track 4).
  4. Day 14-21: Approvals roll in. Capture tokens. Drop into .env.local (or Vercel env post-migration). Smoke-test publishing + DM receive.

Prereqs (block all Meta tracks; do these FIRST)

Meta Business Manager + Business Verification

  • Open https://business.facebook.com — confirm Quarb / Michiu Business Manager exists. If not, create one.
  • Business Info → Submit verification documents. Meta needs proof the business is real:
    • Business registration / KvK extract (Dutch chamber of commerce — Michiu is registered in NL)
    • Utility bill OR phone bill in the business name (matching address)
    • Business website URL: michiu.nl
  • Wait for Business Verification to complete. This is the long pole — 3-7 days typical. App reviews can be submitted in parallel but won't be granted until this clears.
  • Confirm a public URL hosts a privacy policy + terms of service. marketing.qhosty.com is Cloudflare-Access-gated → will fail Meta review. If michiu.nl doesn't have these pages, add /privacy and /terms to the public site repo (michiu-website).

App in Meta for Developers

  • At https://developers.facebook.com/apps/, create app "Michiu Marketing" (or use existing if present).
    • App type: Business
    • Link the app to the Michiu Business Manager (mandatory for the permissions below)
  • Add products: Facebook Login, Instagram Graph API, Messenger, WhatsApp

Track 1 — Meta app review for SOCIAL lane (publishing)

Permissions to request, one at a time, in the app dashboard's App Review → Permissions and Features:

  • pages_show_list — list pages the connecting user manages
  • pages_read_engagement — read engagement signals
  • pages_manage_posts — publish to FB Page
  • instagram_basic — read IG account info
  • instagram_content_publish — publish to IG (feed / reel / story)

For each permission, the form asks for:

  • Use case description — paste roughly:

    "Michiu uses this app to schedule and publish branded content to its own Facebook Page and Instagram Business account from an internal admin console. Posts are drafted by staff, AI-generated captions are reviewed and approved by a human, then queued for publishing via this API."

  • Screencast — Meta's #1 rejection reason. Record the /admin/posts flow end-to-end: paste content → 3 variants generated → approve one → schedule → see "scheduled" in calendar → simulate publish (run pnpm cron:tick for the demo).
  • Test instructions — give the reviewer a working test account on whatever URL the admin console runs at by submission time. Mock-mode is acceptable for the demo since reviewers care about the UX, not whether bytes flew.
  • Privacy policy URL + Terms URL — public, no auth gate.

After approval, capture and drop into .env.local:

  • Page access token — generate a long-lived (~60 days) token via Graph Explorer, OR generate a System-User token under Business Manager → System Users (no expiry). Prefer system-user. Maps to META_PAGE_ACCESS_TOKEN.
  • META_FACEBOOK_PAGE_ID — Page → About → Page ID
  • META_INSTAGRAM_BUSINESS_ID — IG must be linked to the FB Page; ID is in Page Settings → Linked Accounts
  • META_GRAPH_API_VERSION — already defaults to v22.0 in the code. Bump if Meta deprecates.

Timing: ~1-2 weeks after submission, assuming Business Verification has cleared.

Code state: publishers in src/lib/posts/publishers/meta/ (PR #29). All four channels (instagram-feed, instagram-reel, instagram-story, facebook) ready. Will throw MetaConfigError until env vars exist.


Track 2 — Meta app review for DM-INBOX lane (slice 3 webhook)

Same Meta Business Manager + same app. Different permission set, separate App Review submissions.

  • pages_messaging — receive + send FB Messenger messages on behalf of the Page
  • instagram_manage_messages — receive + send IG DMs

For each permission, the form asks for:

  • Use case description — paste roughly:

    "Michiu's admin console receives inbound DMs from guests via Messenger and Instagram, classifies intent (booking inquiry / compliment / question / complaint / etc.), and proposes brand-voice replies that staff approve before sending. Sending uses the standard 24-hour customer-service window."

  • Screencast — record the /admin/dms flow once slice 2 (PR #41) is merged: paste an inbound DM → enrichment + 3 variants render → approve one → "Mark replied manually" or send. Mock-mode is fine for the demo.
  • Test instructions + privacy/terms — same as Track 1.

Webhook setup (slice 3 work — do AFTER approval):

  • In the app dashboard → Messenger product → Webhooks, register https://<your-vercel-domain>/api/webhooks/messenger once Vercel hosts the admin app.
  • Generate META_VERIFY_TOKEN (any random string of your choosing) for the GET handshake.
  • Copy META_APP_SECRET from app dashboard → Settings → Basic — used for X-Hub-Signature-256 HMAC verification on every inbound webhook.
  • Subscribe to webhook fields: messages, messaging_postbacks (FB); messages (IG).
  • Under Messenger → Settings, "Add or Remove Pages" → add the Michiu FB Page. Repeat for IG under the Instagram product.

After approval, capture:

  • Same page access token as Track 1 (scopes overlap — likely a single system-user token works for both lanes).

Timing: ~1-2 weeks after submission. Cannot live-test until Vercel migration also lands (webhook needs a public HTTPS endpoint).

Code state: none yet — slice 3 work. Lane operating contract: .claude/lanes/dm-inbox.CLAUDE.md.


Track 3 — LinkedIn Marketing Developer Platform

Independent of Meta — different ecosystem, same approval pattern.

  • At https://developer.linkedin.comMy Apps, create app "Michiu Marketing" linked to the Michiu LinkedIn Company Page.
    • The Company Page must be verified.
    • The submitter must be a Page admin.
  • Apply for Marketing Developer Platform: https://www.linkedin.com/developers/products/marketing-developer-platform
    • Use case: "Posting branded organization content to the Michiu LinkedIn Company Page from an internal admin tool with approval-gated drafts."
    • Brief description of the company + plan to use the API.
    • Some reviewers ask for a screencast — have one ready.
  • Required scope: w_organization_social (post on behalf of org). Optional but useful: r_organization_social (read posts back), rw_organization_admin (manage page).
  • Add an OAuth 2.0 redirect URL in app settings — must match where the auth dance runs (e.g., http://localhost:3000/api/linkedin/callback for local dev).

After approval, capture and drop into .env.local:

  • Run the OAuth 2.0 3-legged auth flow once to get an access token (~60-day TTL; refresh tokens extend it). The dev portal has a "Generate access token" button that handles this for testing — fine for production single-tenant use.
  • LINKEDIN_ACCESS_TOKEN — the bearer token.
  • LINKEDIN_ORGANIZATION_ID — find by visiting https://www.linkedin.com/company/<slug>/admin/ and copying the numeric ID from the URL. Code accepts either bare numeric or full urn:li:organization:{id}.
  • LINKEDIN_API_VERSION — already defaults to 202410 in the code; bump if LinkedIn rolls forward.

Timing: ~1-2 weeks. Independent of Meta.

Code state: publisher in src/lib/posts/publishers/linkedin.ts (PR #30). Single + multi-image flows ready; video deferred. Will throw a config error until env vars exist.


Track 4 — WhatsApp Business API (replaces Slack for SLA alerts per #39)

Same Meta Business Manager + same app, separate WhatsApp product flow.

  • In the Meta app dashboard, add the WhatsApp product if not already added.
  • Provision a phone number — three options:
    • Buy a new number through Meta (cleanest for a fresh business line).
    • Port an existing landline / mobile — must NOT have been used on consumer WhatsApp in the last ~30 days, or it'll be rejected.
    • Use Meta's free test number for development first.
  • Display name approval — submit "Michiu" or "Restaurant Michiu". Approval is 1-3 days. Must comply with WhatsApp Business display name guidelines — no generic words, no profanity, must match registered business name.
  • Message templates — for SLA alerts to staff, need a utility template, e.g.:
    • Template name: urgent_review_alert
    • Category: Utility
    • Body: "Urgent review at {{1}}: {{2}}-star from {{3}}. {{4}}" with a button to open the admin console.
    • Submit for approval — 1-3 days per template.

After approval, capture:

  • WABA ID (WhatsApp Business Account ID) — Meta Business Manager → WhatsApp Manager.
  • Phone Number ID — same place, distinct from WABA ID.
  • System User access token — Meta Business Manager → System Users → create a system user → assign WhatsApp permissions → generate token (no expiry). Recommended over user tokens.
  • When the adapter PR lands, add WHATSAPP_PHONE_NUMBER_ID, WHATSAPP_ACCESS_TOKEN, WHATSAPP_RECIPIENT_PHONE (staff WhatsApp number) to .env.example.

Timing: Phone + display name 1-3 days; templates 1-3 days each. Total ~1 week if no rejections.

Code state: none yet. Per #39, the WhatsApp adapter waits for verified API access before being written (lesson from #34 — don't pre-write against guessed API shapes). PR #7's Slack code stays merged as a no-op.


What I (Claude) can do once tokens exist

When the env vars are populated:

  • Smoke-test the social lane against a single test post on each channel (pnpm cron:tick against a real posts.scheduled_for = now).
  • Write the WhatsApp notification adapter — likely lives at src/lib/notifications/whatsapp.ts parallel to the existing Slack code in src/lib/reviews/notifications.ts.
  • Bump dm-enrich-v1.0v2.0 once we have real DMs to calibrate the prompt against (build the regression suite alongside per the dm-inbox lane's known-gaps list).

What's NOT in this checklist

  • Vercel migration — separate workstream. Deferred per user 2026-05-05 ("transfer to Versel when everything is finished"). Track 2's webhook receiver and the cron worker for Track 1 both block on this for production. Local-only dev unblocks during the wait.
  • Anthropic credit top-up — not an "approval", just a billing action. Caption generator + enrich-v2 vision + DM enrichment all fall back to mock without it. https://console.anthropic.com/settings/billing
  • Zenchef partner-API access — guest-intel lane, separate ask via api-tech-help@zenchef.com. Tracked in .claude/lanes/handoffs/guest-intel.HANDOFF.md.
  • Google Ads API dev token — ads lane, Q1-Q2 2027. Tracked in .claude/lanes/handoffs/ads.HANDOFF.md.

Source: michiu-marketing/docs/platform/external-approvals-checklist.md. To edit, change the file in the repo and redeploy.