Docs

Composio setup

3x talks to the ad platforms through Composio, which stores the OAuth tokens and runs the platform APIs as versioned tools. This is configured once per 3x deployment by the operator; tenants then connect their own ad accounts from the dashboard without ever seeing a Composio key.

1. Project API key

  1. In the Composio dashboard create a project for the deployment (e.g. "3x production").
  2. Settings → API keys → create a project key (ak_…) and set it as COMPOSIO_API_KEY on the API.

Keys are sent only as headers, never logged and never stored in the database. A user key (uak_…, as used by the Composio CLI) also works for local development, but then COMPOSIO_PROJECT_ID must be set, because a user key without project id reaches an empty default project.

2. One auth config per platform

PlatformToolkitAuth configVariable
Google AdsgoogleadsComposio-managed OAuth is fine (scope adwords)COMPOSIO_AUTH_CONFIG_GOOGLEADS
Meta Adsmetaadsnot managed: your own Meta app (OAuth, ads_read,business_management) or API-key mode with a system-user tokenCOMPOSIO_AUTH_CONFIG_METAADS
LinkedIn Adslinkedin_adsnot managed: your own LinkedIn app with the Advertising API product (and Conversions API with rw_conversions if you upload conversions through Composio)COMPOSIO_AUTH_CONFIG_LINKEDIN_ADS
TikTok Adstiktok_adsComposio-managed OAuth is fine for reportingCOMPOSIO_AUTH_CONFIG_TIKTOK_ADS

Create them under Auth configs → New in the Composio dashboard and copy the ac_… ids. A platform without an auth config is simply not offered in the dashboard; its connect route answers 503 not_configured.

LinkedIn and Meta app reviews take time (LinkedIn's Advertising API review often several weeks). Start them before you need the data – there is no fallback source for LinkedIn spend.

3. Pin toolkit versions

Composio versions its toolkits; a new version can change response shapes. 3x pins the versions it was tested with:

COMPOSIO_TOOLKIT_VERSIONS={"googleads":"20261014_00","metaads":"20261008_00","linkedin_ads":"20260826_00","tiktok_ads":"20261001_00"}

Raise a version only after running the normalizer contract tests (npm test -w @3x/api) against a recorded sample of the new version.

4. Webhook (connection health)

Create one webhook subscription in the Composio project for composio.connected_account.expired and composio.connected_account.activated, pointing at

https://<api-host>/v1/webhooks/composio

and set its signing secret as COMPOSIO_WEBHOOK_SECRET (a whsec_ prefix is accepted). 3x verifies the Standard Webhooks signature (webhook-id, webhook-timestamp, webhook-signature), rejects timestamps older than five minutes and ignores replays. Without the webhook, the daily ads.health job still detects expired connections – just later.

5. Check

Open Settings → Integrations of a project in the dashboard and click Connect on a platform. A redirect to Composio's hosted login means the key and the auth config work; 503 not_configured names the missing variable. Starting a connection (POST /v1/projects/{project}/ad-connections) requires a signed-in user with ads:write – secret keys cannot start an OAuth flow on someone's behalf.

How connections work

  • Organization-level connections belong to an organization (agencies: one MCC or Business Manager login). Every project of the organization can then select its own ad accounts from it.
  • Project-level connections belong to one project.
  • The connect link is generated on click and expires after 10 minutes. After Composio's hosted OAuth, 3x verifies the callback against a one-time nonce, waits until the connection is ACTIVE, discovers the reachable ad accounts and lets you select those that belong to the project.
  • Disconnecting revokes the Composio connection and stops syncing; the imported cost data stays until you delete it.

Next: connect Google, Meta, LinkedIn or TikTok.