Investigated a "redirect_uri does not match the registered value" error
on a second laptop. Traced the code fully rather than assume a bug:
_linkedin_redirect_uri() computes one value, the frontend already
displays that exact value labeled "Add this callback URL in LinkedIn
Developer Portal," and the OAuth start endpoint sends that same value —
internally consistent, nothing to fix in application code.
Found the real cause instead: two separate LinkedIn login mechanisms
exist in this codebase from different points in its history — an old
terminal script (scripts/start-linkedin-oauth.ps1, port 44002, via the
linkedin-mcp-server npm package) and the current in-app button (port
8088, handled directly by the backend). The LinkedIn Developer Portal app
only ever had the old URL registered.
Documents both mechanisms side by side so they're never conflated again,
and records that this fix is shared across every machine using the same
LinkedIn app — unlike Google Cloud credentials, which are per-machine.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Written from an actual first-time setup on a second machine, in order,
including every wall that was hit and the exact click-path past it: the
two authentication methods, the two separate (legacy + managed)
organization policies that can block service account key creation, the
Vertex AI User role requirement, and a troubleshooting table mapping each
literal error message we saw to its real cause.
Leads with the one fact that caused most of the confusion: none of this
setup travels with git pull. Every machine running MaskanX needs it done
locally, once, even with identical code.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Code 100 / subcode 1885183 stopped the connection test after nine
consecutive field-mapping fixes, and it is not one: "Ads creative post was
created by an app that is in development mode. It must be in public to
create this ad." Decoded the access token via /debug_token to confirm
which app — MaskanXAds Integration, id 1592299622228272 — rather than
guess. Toggling it to Live on developers.facebook.com is the only fix;
nothing in MaskanX can do this on the user's behalf.
Documented next to the payment-method gate it sits beside in the setup
guide.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A live sync against the real account failed with Meta code 100, subcode
4834011, and the only thing we stored was the literal string "Invalid
parameter". Meta had written an explanation — it puts one in
error_user_title and error_user_msg — and MetaError was discarding it.
Every explanatory field is now kept, describe() prefers the text written
for a person, and as_dict() carries the lot into the API response and the
stored sync_error.
That still left diagnosis needing a terminal, which is no good: the person
who has to attach a payment method or fix a Page id is a client, not a
developer. GET /campaigns/diagnostics runs seven ordered readiness checks
and returns a remedy with each failure. It stops at the first hard failure
— with no token every later check fails for the same reason, and five
identical errors hide the one that matters.
POST /campaigns/diagnostics/smoke-test closes the remaining gap: the unit
suite proves we send what we think we send, not that Meta accepts it. It
builds the real chain, reads back from Graph that every object is PAUSED,
and deletes in a finally. If cleanup fails, the ids come back in the
response rather than being abandoned in a real ad account.
Both routes are declared above /{campaign_id}. /diagnostics is a single
path segment, so the wildcard would otherwise answer it — the same
shadowing /discover and /adopt are already guarded against. Four tests go
through HTTP to pin the ordering, because the rest of this file's tests
call the functions directly and would pass against an unreachable
endpoint.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Setup, first campaign, daily use, and the chat prompts — including the
MCP path for adding CRM fields from a conversation.
Records the two things currently blocking a live launch: the ad account
has no payment method, which only Business Manager can fix, and the
operator token has to be set before an approval can authorise spend.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>