Skip to content
InfluToDocs

Reference

HTTP API reference

The /api/sdk endpoints behind every InfluTo SDK, the RevenueCat and Google Play webhooks InfluTo receives, and where the agent REST API is documented.

The SDKs are thin clients over a small HTTP API. Use this page when your stack has no SDK, or to see what an SDK sends while you debug. If an SDK exists for your platform, use it — it also stores the attribution, sets RevenueCat attributes and de-duplicates for you. The full wire contract, including the rules every SDK follows, is at docs.influ.to/contract.md (wire version 1.6.0).

Basics

  • Base URL: https://influ.to/api
  • Auth: Authorization: Bearer <SDK key> on every request. The key starts with it_ and is on Setup & SDK; one key per app covers sandbox and production. It only works on the /api/sdk/* endpoints of its own app. It ships inside your app, so treat it as public.
  • Format: JSON bodies with Content-Type: application/json. Field names are snake_case. /sdk/event and /sdk/purchase also accept camelCase, which is what the SDKs send there.
  • Errors: {"detail": "…"} with the status code. A missing or mistyped field is a 422 with the field list in detail. A request over the rate limit gets a plain 429 page. All codes: Limits and errors.
  • The server reads the client IP and User-Agent from the request itself; don't put them in the body.
Terminal
curl https://influ.to/api/sdk/validate-code \
  -H "Authorization: Bearer $INFLUTO_SDK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code": "MAYA"}'

Endpoints

Method Path Purpose SDK method
POST /sdk/init Start-up check; says whether the platform is store-direct initialize
POST /sdk/track-install Decide whether this install was referred checkAttribution
POST /sdk/identify Attach your user ID to the install identifyUser
POST /sdk/event Record a custom event trackEvent
POST /sdk/validate-code Check a typed code validateCode
POST /sdk/set-referral-code Apply a code; redeem a free-access code setReferralCode
POST /sdk/purchase Report a store purchase (store-direct) reportPurchase
GET /sdk/access Whether a user has free access checkAccess
GET /sdk/campaigns Active public campaigns getActiveCampaigns
GET /sdk/attribution A paying user's attribution —
GET /sdk/recent-conversions A user's recent purchase outcomes, for testing —

POST /sdk/init

Call once per app start. It marks the SDK as installed on the Setup page and tells the client whether this platform uses store-direct validation.

Field Required Notes
app_version Yes Your app's version ("unknown" if you don't have it)
sdk_version Yes Your client's version
platform Yes ios, android or web
sdk_name, runtime No Sent by the web SDK (web; capacitor, cordova, twa, pwa or browser)
JSON
{
  "app_id": "3f6c…",
  "app_name": "Your App",
  "attribution_window_hours": 24,
  "campaigns": [{ "id": "a1b2…", "name": "Launch", "description": null }],
  "store_direct": false,
  "initialized": true
}

campaigns lists every active campaign of the app. store_direct is true when this platform validates purchases directly with Apple or Google; the SDKs start automatic purchase capture only then.

POST /sdk/track-install

Decides once per install whether a creator referred it — by the code a link carried into the app, or by link-click matching within 72 hours. Call it on first launch and store the answer; the SDKs never call it again after they have one.

Field Required Notes
platform Yes ios, android or web
device_id Recommended A random UUID you generate once per install and persist. Installs are counted once per device_id.
device_brand, device_model, os_version, screen_resolution, timezone, language No Device details the SDKs send for matching. camelCase also accepted.
referral_code, click_id No From a link that carried the code into the app (influto_code / influto_click on a web app's URL, or the Play Install Referrer). The install is then attributed exactly (link_param).
JSON
{
  "attributed": true,
  "referral_code": "MAYA",
  "attribution_method": "ip_device_match",
  "clicked_at": "2026-10-09T08:12:44.120000",
  "click_id": "9d2e…",
  "install_recorded": true,
  "message": "Install attributed successfully"
}
  • Organic: {"attributed": false, "install_recorded": true, "message": "No matching referral click found (organic install)"}.
  • install_recorded is false when this device_id was seen before.
  • A device that already claimed a click within the window gets the same answer again, with "re_claimed": true — so a lost response can be fetched again without taking someone else's click.
  • attribution_method: link_param or ip_device_match (link-click matching). See attribution.

POST /sdk/identify

Attaches your user ID — usually the RevenueCat app user ID — to the install. When a RevenueCat event later arrives without the influto_code attribute, InfluTo looks for an attributed click with that user ID.

Field Required Notes
app_user_id Yes Your user ID. Empty is a 400.
properties No Object, stored for your reference
device_id (+ platform and the device fields) No Send the same device_id you sent to track-install and the server matches this exact install. Without it, it falls back to the request's IP and only uses an unambiguous, recent match.
JSON
{ "identified": true, "app_user_id": "user_123", "attribution_backfilled": true }

The first identify for a click wins. The same key and user identified again within 60 seconds is answered from memory with "deduplicated": true. This endpoint has its own, lower rate limit — 2 requests per second per IP — so call it once per launch, not in a loop.

POST /sdk/event

Records a custom event for the dashboard funnel. It never creates a conversion or a commission.

Field Required Notes
event_type / eventType Yes For example paywall_viewed, trial_started
app_user_id / appUserId Yes Your user ID
properties No Object
referral_code / referralCode No Code to associate
event_id / eventId No Your idempotency key — a repeat with the same ID is dropped
JSON
{ "tracked": true, "event_id": "c71a…" }

A dropped repeat answers {"tracked": true, "event_id": "…", "duplicate": true, "dedup_reason": "…"}. Besides event_id, the server keeps trial_started, subscription_purchased and subscription_renewed to one per user and property set, and drops an identical event without an event_id repeated within 60 seconds.

POST /sdk/validate-code

Checks a code a user typed, against the app's active creator codes and then its free-access codes. Always 200 (unless the request itself is rejected); look at valid.

Field Required Notes
code Yes The server uppercases it and drops characters outside A–Z 0–9 _ -

Creator code:

JSON
{
  "valid": true,
  "code": "MAYA",
  "campaign": { "id": "a1b2…", "name": "Launch", "description": null, "commission_percentage": 30.0, "campaign_type": "public" },
  "influencer": { "name": "Maya R.", "social_handle": "@maya", "follower_count": null },
  "custom_data": null,
  "message": "Valid code! Referred by Maya R."
}

A free-access code returns campaign.name "Free Access", campaign.campaign_type "FREE_ACCESS", commission_percentage 0, influencer: null and the code's custom_data. influencer and message contain the creator's personal name — don't show them to users unless creators agreed.

Invalid: {"valid": false, "error": "Code not found or inactive", "error_code": "CODE_NOT_FOUND"}. The error_code values are INVALID_FORMAT (not 4–20 characters), CODE_NOT_FOUND and CODE_EXPIRED (a free-access code that expired or has no seats left).

POST /sdk/set-referral-code

Applies a code for this user. For a creator code it records the attribution; for a free-access code it grants the free access.

Field Required Notes
code Yes Same cleanup as validate-code
app_user_id For free-access codes Without it a free-access code fails with MISSING_USER_ID

Creator code:

JSON
{ "success": true, "code": "MAYA", "message": "Referral code set successfully", "campaign": { "id": "a1b2…", "name": "Launch" } }

If this creator's link already attributed an install from the same IP within the window, nothing new is recorded: "message": "Code already attributed from link click" with "already_attributed": true. A new manual entry counts as a click and an install for the creator.

Free-access code:

JSON
{
  "success": true,
  "code": "PRESS2026",
  "free_access": true,
  "grants_access": true,
  "already_granted": false,
  "entitlement": "premium",
  "access_duration_years": 1,
  "access_duration_days": 365,
  "expires_at": "2027-10-09T08:15:02",
  "rc_grant": { "attempted": true, "ok": true, "error": null },
  "message": "Free access activated — 1 year of premium."
}
  • Access is granted in InfluTo whether or not RevenueCat is involved; rc_grant only reports the optional RevenueCat promotional-entitlement mirror. Gate premium on GET /sdk/access, not on rc_grant.
  • Redeeming the same code again for the same user re-grants without using another seat (already_granted: true).
  • A free-access failure is still 200, with success: false, free_access: true, and error_code CODE_EXPIRED or MISSING_USER_ID.
  • An unknown or inactive code is 404 {"detail": "Referral code not found or inactive"}.

POST /sdk/purchase

Store-direct only. Sends the store's signed proof of a purchase; InfluTo verifies it with Apple or Google, converts the price to USD at the purchase date's rate, and records it exactly like a RevenueCat event. See store-direct.

Field Required Notes
platform Yes ios or android
signed_transaction / signedTransaction iOS StoreKit 2 jwsRepresentation (the signed JWS)
purchase_token / purchaseToken Android Play Billing purchaseToken
product_id / productId Android one-time products Routes the purchase to one-time validation. Omit for subscriptions.
price, currency Android one-time products Price paid (major units) and ISO currency, from ProductDetails — Google doesn't return a one-time price
referral_code / referralCode First purchase The stored referral code, so the purchase binds to the creator
app_user_id / appUserId No Your user ID; also makes the purchase show up in recent-conversions
JSON
{
  "success": true,
  "validated": "apple",
  "environment": "PRODUCTION",
  "event_type": "INITIAL_PURCHASE",
  "duplicate": false,
  "recorded": true,
  "result": { "processed": true, "event_id": "…" }
}
Status Meaning
200 Verified and processed. duplicate: true means it was already recorded — reporting twice is safe.
400 The platform isn't switched to store-direct yet (switching needs a green credential check), credentials are missing, the proof is missing, verification failed, or the platform isn't ios/android.
409 Android one-time purchase that isn't in the PURCHASED state (for example, still pending).
503 The exchange rate isn't available right now. Retry later.

Apple purchases are keyed on the original transaction ID, Google subscriptions on the purchase token, Google one-time purchases on the order ID — so renewals and refunds found later attach to the same conversion.

GET /sdk/access

/sdk/access?app_user_id=user_123 — whether InfluTo currently grants this user free access from a free-access code.

JSON
{ "has_access": true, "source": "comp", "entitlement": "premium", "expires_at": "2027-10-09T08:15:02", "code": "PRESS2026" }

Without access: {"has_access": false, "source": null, "entitlement": null, "expires_at": null, "code": null}. Seat limits and expiry are checked on every call; a blocked or expired code removes access. An empty app_user_id is a 400.

GET /sdk/campaigns

The app's active public campaigns, for showing offers in the app.

JSON
[{ "id": "a1b2…", "name": "Launch", "description": null, "commission_percentage": 30.0 }]

GET /sdk/attribution

/sdk/attribution?app_user_id=user_123 — which creator a paying user is attributed to. It reads conversions, so it answers attributed: true only once an attributed purchase exists.

JSON
{
  "attributed": true,
  "referral_code": "MAYA",
  "campaign": { "id": "a1b2…", "name": "Launch" },
  "attribution_timestamp": "2026-10-09T09:01:13",
  "attribution_method": "ip_device_match"
}

Otherwise: {"attributed": false, "message": "User has no attribution (organic user)"}.

GET /sdk/recent-conversions

/sdk/recent-conversions?app_user_id=user_123&limit=10 — the user's latest purchase events in this app, newest first, so a test build can confirm a purchase reached InfluTo. limit is 1–50 (default 10). No amounts and no creator names.

JSON
{
  "app_user_id": "user_123",
  "count": 1,
  "attributed_count": 1,
  "conversions": [{
    "event_type": "INITIAL_PURCHASE", "product_id": "pro_monthly", "store": "APP_STORE",
    "environment": "SANDBOX", "is_test": true, "attributed": true, "referral_code": "MAYA",
    "status": "active", "event_timestamp": "2026-10-09T09:01:13"
  }]
}

Inbound webhooks

InfluTo receives purchase events from RevenueCat; on store-direct apps it checks purchases with Apple and Google itself. It sends no webhooks to your servers. To follow what happens, use Real-Time Events in the dashboard or the agent API.

RevenueCat

POST https://influ.to/api/webhooks/revenuecat/{app_id} — Setup & SDK shows the full URL with your app's ID. Set it up as described in RevenueCat.

  • Auth: RevenueCat's "Authorization header value" must be Bearer rc_webhook_…, the secret InfluTo generates under Setup & SDK → Webhook (shown once; regenerate it there or in Settings → Integration). The bare token, a lowercase bearer and stray whitespace are accepted too. Rejected calls are counted and shown on the Setup page.
  • Body: RevenueCat's standard webhook JSON. InfluTo reads the code from the subscriber attribute influto_code. influto_referral is for your own RevenueCat targeting; InfluTo doesn't read it. Without a code, it looks for an attributed click with the event's app user ID — see /sdk/identify. A user who already converted for a creator stays with that creator, whatever code arrives later.
  • Environments: send both. SANDBOX, XCODE and LOCAL_TESTING events go through attribution but never reserve money or count in stats.
Event What InfluTo does
TEST Marks the webhook as connected
INITIAL_PURCHASE Creates the conversion when the user is attributed; a paid purchase earns commission, a trial waits until it converts
RENEWAL Commission for each paid renewal
NON_RENEWING_PURCHASE Commission for each one-time purchase
CANCELLATION, UNCANCELLATION, EXPIRATION Updates the subscription's state
PRODUCT_CHANGE Updates the product; the next renewal's price sets the commission
BILLING_ISSUE Logged
REFUND Reverses the commission

Promotional grants (store PROMOTIONAL, including the ones InfluTo makes for free-access codes) are acknowledged and ignored, as are event types not in the table.

Response When
200 {"received": true, "event_id": "…"} Accepted and processed
200 {"received": true, "error": "…", "event_id": "…"} Accepted, but processing failed. Not retried by RevenueCat or by InfluTo — the error is in the response body in RevenueCat's delivery log.
400 The body isn't JSON
401 Missing or wrong Authorization value; the body says how to fix it
404 No app with this ID

Events are idempotent on app, event type, transaction ID and event timestamp, so a redelivered event is recorded once.

Agent REST API

The API that AI agents and scripts use to run your program — apps, campaigns, creators, analytics, integration status — lives under https://influ.to/api/agent/*. It uses an agent key (it_mcp_…, from Connect AI Agent) or an OAuth token, never the SDK key. The MCP tools that read or change your account are wrappers over these endpoints.