Partner integrations

How partner connections are made, verified and monitored — OAuth, manual credentials, live verification, deliveries, test events and failure handling.

Updated 2026-09-02

WhichClick connects to 70+ partners (self-attributing networks, ad networks, DSPs, CRMs, analytics tools). Every connection is treated as untrusted until it is verified against the partner and stays monitored afterwards.

Connecting#

MethodPartnersHow
OAuth (Connect with …)Google Ads, Meta, TikTok, Snapchat, Pinterest, Reddit, LinkedInOne click from Partners → {partner}. Tokens are stored encrypted, the account id is auto-discovered when the token can see exactly one account, and the connection is verified before it is enabled. Requires the partner app credentials on the server (OAUTH_* variables, see Self-hosting).
Manual credentialsAllPaste the fields from the partner dashboard. Saving runs the same verification.
REST / MCPIntegrations are managed in the dashboard only; API keys cannot create partner connections.

Verification#

Saving, the OAuth callback, Verify now and a daily scheduler job all run the same check:

LevelWhat happensPartners
apiA real read call with the stored credentials (customer / advertiser / account lookup, or an ingestion call in validate mode). Returns the account label shown in the UI.Google Ads, Meta, TikTok, Snap, Apple Search Ads, Pinterest, Reddit, LinkedIn, Klaviyo, Braze, Iterable, OneSignal, Mixpanel, Amplitude, Segment, GA4, CleverTap, AppLovin, Unity Ads, ironSource, Mintegral
test_messageA message is posted to the channel.Slack
reachabilityPostback URL templates are rendered with sample values, the host must resolve and answer over TLS. Credentials themselves cannot be checked.Ad networks, DSPs, affiliate platforms, custom webhook
nonePartner needs no credentials (attribution comes from the SDK).Huawei AppGallery, Xiaomi GetApps

A failed verification stores the integration disabled with status = error and the exact reason. There is no partial or assumed state.

Deliveries#

Every outbound call is a row in the delivery log (dashboard: Integrations → Recent deliveries, per partner under Deliveries):

KindTrigger
install, eventInstall / in-app event forwarded (SAN Conversions APIs, native analytics payloads, or postback templates)
testSend test event — a purchase of 1.00 using the partner's test mode where one exists (Meta / TikTok test_event_code, Snap validate endpoint)
verifyVerification result
costCost sync pull with the number of campaign-day rows
protect_syncGoogle Ads negative-IP exclusions and campaign pauses

Success clears lastError; a failure increments the failure counter and after five consecutive failures the integration flips to status = error (visible on the Integrations page and in alerts). Skips (event disabled by mapping, install not attributed to the partner, missing consent for Google's app conversion API) are not deliveries and are not counted.

Event forwarding formats#

PartnerFormat
MetaConversions API for App Events (action_source=app, anon_id, extinfo)
TikTokEvents API v1.3 (event_source=app, ttclid)
SnapchatConversions API v3 (action_source=MOBILE_APP, hashed ids)
Google AdsApp Conversion Tracking API (per-app link id; needs the raw advertising id under consent)
Amplitude, Mixpanel, Segment, GA4 (Measurement Protocol), Klaviyo, Braze, Iterable, CleverTap, MoEngage, Airship, OneSignal, MailchimpNative HTTP APIs with the documented payloads
Everything elseDocumented postback URL template with {macros} (GET or POST)

Event names can be renamed or disabled per partner (eventMapping), and forwarding can be limited to installs attributed to that partner (onlyAttributed).

Cost sync#

Automatic daily spend requires the cost capability, which is only offered where a real fetcher exists: Google Ads (GAQL), Meta (Insights), TikTok (Reporting), Apple Search Ads (Campaign Management API), AppLovin, Unity Ads, ironSource and Mintegral. Currencies always come from the partner API; a row without a currency aborts the run rather than defaulting. Other partners import spend via CSV or POST /api/v1/costs. See Cost import & sync.

The google_ads integration is the single credential store for GAQL cost sync, Protect's negative-IP exclusions and incident auto-pause. Connect with Google (OAuth) or paste an OAuth client + refresh token; the developer token can be supplied per organization or platform-wide (GOOGLE_ADS_DEVELOPER_TOKEN). The App Conversion Tracking link id remains per app under Apps & SDK.

Security#

  • Credentials are AES-256-GCM encrypted with CREDENTIALS_KEY; only the prefix of a secret is ever displayed.
  • OAuth state is HMAC-signed with the organization and user id and expires after 15 minutes.
  • Short-lived tokens (Snap, Pinterest, Reddit) are refreshed automatically before use and the new tokens are persisted.
  • Every connect, verify, test, enable/disable and disconnect is written to the audit log.