Server-to-server events API for mobile (S2S-mobile)

At a glance: The Server-to-Server (S2S) events for mobile API sends events occurring outside the app to AppsFlyer. S2S events are attributed similarly to SDK events and are available across the platform. 

S2S_us-en.png

Server-to-server events API for mobile

The AppsFlyer platform attributes and records mobile app events sent by the AppsFlyer SDK and by APIs. Use the S2S API to report events that take place outside the app; for example, a user renews their subscription using your web interface. S2S events, once recorded, are available across the platform, including dashboards, raw data, and analytics. For PBA web events see Web S2S for PBA.

AppsFlyer populates S2S events with:

  • Values sent in the S2S message
  • Some AppsFlyer install attribution values like, install time and media source.

API use instructions

Create your API call using the information contained in the sections that follow. 

S2S API facts

API endpoint

https://api2.appsflyer.com/inappevent/app_id

  • app_id: The app identifier used in the AppsFlyer dashboard. Insert it precisely as it appears on the dashboard.
  • iOS apps: Ensure to prefix with id.  Failure to do so results in a valid 200 OK return code; without recording the event.
  • Windows: Get the app ID from the Microsoft App Store.
  • Example Android: https://api2.appsflyer.com/inappevent/com.appsflyer.myapp
    iOS:https://api2.appsflyer.com/inappevent/id123456789
    Windows:https://api2.appsflyer.com/inappevent/a1b2c3d4e5f6
HTTP method POST
Accepted content type application/json
Authorization

--header 'authentication: dev_key'

  • The dev_key is the authentication token in the header. 
  • To get the dev key, in AppsFlyer go to, App Settings > Dev Key
Allowlist servers to get response messages

Allowlist AWS IP address Ranges to get response messages if your servers are located behind a firewall. 

JSON payload limitation 

JSON payload size: up to1KB

Rate limitation

POST limitation volume: 60,000 POSTs per minute. To increase this limit, contact your CSM.

Coding instructions
Encode URLs

Encode (percent) code reserved characters (https://tools.ietf.org/html/rfc3986#section-2.1) before forming the method URL. 

TLS

Use TLS 1.2 or above. Ciphers supported

S2S API facts

Payload parameters

  • Payload parameters consist of one or more device identifiers (depending on the operating system).
  • What if I can't send a device identifier?
    • You may be unable to send the identifier for a reason out of your control, for example, because the user has limited ad tracking (LAT) or uses iOS 14, and did not give ATT consent (no IDFA).
    • If this is the case, be aware that not  sending an advertising ID/device identifier can cause attribution failure,  meaning that AppsFlyer can't attribute the event to the media source Events are recorded and do display in reports and dashboards. 
Operating system Identifier name Description

iOS

iconAFios.png

 

idfa 

Where available populate with the device IDFA

Format: String

Example: "idfa": "9876F1SS-2983-3855-27RR-2R626772VFNB"

idfv

Where available populate with the device IDFV.

Format: String
Example: "idfv": "95C9BD22-4A4C-41C8-9548-ED07C5C8C145"

Android

iconAFand.png

advertising_id

Where available populate with the device GAID (advertising ID)

Format: String

Example: "advertising_id": "9c9a82fb-d5de-4cd1-90c3-527441c11828"

oaid

Format: String

Example: "oaid": "1fe9a970-efbb-29e0-0bdd-f5dbbf751ab5"

amazon_aid

Format: String

imei

Format: String

Example: "imei": "AA-BBBBBB-CCCCCC-D"

Device identifiers
Parameter Name Mandatory Description

appsflyer_id

Yes

A unique identifier, generated by AppsFlyer, when the app launches for the first time. 

  • Format: String
  • Example: "appsflyer_id": "1415211453000-6513894"

customer_user_id

No

Customer user ID, a unique user identifier set by the app owner.

  • Format: String. 
  • Example: "customer_user_id": "my_customer_number1234" 

att

No

iOS ATTrackingManager authorization status

  • If the device OS version is iOS 14 or later, populate attwith ATTrackingManager.
    • Format: Single-digit integer 
    • Example: 1
  • The iOS values forATTrackingManager are:
    • 0: Not determined
    • 1: Restricted
    • 2: Denied
    • 3: Authorize

Note! 

  • As part of Apple privacy policies, we recommend that you populate att with the ATTrackingManager value. 
  • You can send this parameter prior to the official release of iOS14; we'll begin processing it after the official release. 

 

ip

No

The mobile device's IP address during the event occurrence.

  • If sent, the IP address is used to populate geo fields.
  • If not sent, AppsFlyer populates the IP address and geo fields using the values from the install attribution event. 
  • Format: String containing IPV4 or IPV6 address
  • Example: "ip": "192.0.2.1
af_events_api Deprecated
  • Deprecated starting July 6, 2020.
  • There is no need for this paramter, you can stop sending it. Doing so does not impact attribution in any way. 

eventName

Yes

Specify the event name. Ensure that the event names are aligned with the marketer's requirements.

  • Format: String
  • Example: "eventName": "af_purchase"

eventValue

Yes

If you send an event without a value then send: "eventValue":""

  • Event values need to be sent without additional symbols or formatting. 
  • For af_revenue a decimal comma can be used. Negative values should be preceded by a -
  • Format: Strings in a JSON
  • Example: "eventValue": "{ \"af_revenue\": \"6\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\", \"af_quantity\" :\"1\" }"

app_version_name

No

Your app version or identifier.

  • Format: String
  • Example: "app_version_name": "my_app_version"

app_store

No

Equivalent to AF_STORE in Android apps. The store from which the app was downloaded. 

  • Raw data field: Install App Store
  • Format: String
  • Example: my_android_store_is_best

eventTime

No

The time the event occurred using UTC timezone.

  • Default: If noeventTimeis sent, the time is set to the HTTP message arrival time.
  • Format: string yyyy-mm-dd hh:mm:ss.sss the time needs to be in UTC timezone.
  • Example: "eventTime":"2019-05-15 12:17:01.123" means 12:17:01 UTC. 

Close of day:

  • If you implement eventTime the event must reach AppsFlyer by no later 02:00 of the following day. Meaning, if the event took place on Monday at 17:00 UTC, it must reach AppsFlyer servers by no later than Tuesday 02:00 UTC. 
  • Events received after close of day are stamped with the actual arrival time.
  • Events having a future time, meaning after the arrival time, are stamped with the arrival time. 

Example

  • Times are in UTC
  • An event is sent with eventTime = Monday 21:00.
Time of arrival Time recorded by AppsFlyer Remark
Tuesday 01:00 Monday 21:00 Arrived before close of business.
Wednesday 09:00  Wednesday 09:00 Arrived after close of business. Time is set to arrival time. 

eventCurrency

No

Currency code using ISO 4217 3-character codes and BCN (Bitcoin)

  • Default: USD
  • Example: "eventCurrency": "ZAR"

bundleIdentifier

No

A unique app identifier. In raw-data, the parameter populates Bundle ID.

  • Format: String
  • Example: "bundleIdentifer": "com.myapp"

sharing_filter

No

The sharing filter blocks the sharing of S2S events via postbacks/API with integrated partners and other third-party integrations. 

Use the filter to fulfill regulatory requirements like GDPR and CCPA, to comply with user opt-out mechanisms, and for other business logic reasons. 

The sharing_filter has the following options:

  • all: All partners are blocked. Don't share the event with anyone.  Example: "sharing_filter": "all"
  • List of partner ids in an array. Format ["partnerid_1", "partnerid_2", "partnerid_n"]  Example: "sharing_filter": ["googleadwords_int", "adcolony_int"]

For a list of partner ids, contact your CSM or AppsFlyer support. 

Curl example

curl --location --request POST 'https://api2.appsflyer.com/inappevent/<app_id_placeholder>' \
--header 'authentication: <dev_key_placeholder>
' \
--header 'Content-Type: application/json' \
--data-raw '{
  "appsflyer_id": "9999999999999-9999999999999999999",
	"advertising_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
	"customer_user_id" : "example_customer_id_123",
	"ip": "199.0.2.1",
	"app_version_name" : "example_version_name",
	"eventTime" : "2020-02-25 12:00.000",
	"eventName": "af_purchase",
	"eventCurrency": "ZAR",
	"eventValue": 
	"{
		\"af_revenue\": \"1006\",
		\"af_content_type\": \"wallets\",
		\"af_content_id\": \"15854\",
		\"af_quantity\" :\"1\"
   }"
}
'

Response codes

Response code  Message How to Handle
200 OK

On receipt of a message, minimum data validation is performed. As such, you can get an OK response even though the event may not completely record in AppsFlyer. To debug events:

  • Using the CURL example in this article"
    • Update the app id and dev key details.
    • Change the appsflyer_id, and eventTime parameters. Use the current time and an appsflyer_id recently attributed as non-organic.
  • Send the call.
    • It should return with a 200 OK message.
    • Check to see if the event recorded correctly including the revenue
    • Make additional changes as needed and test again.
400 Failed to Authenticate Ensure that the authentication key is correct.
400  appsflyer_id is a mandatory field
  • Make sure you pass the correct AppsFlyer ID in the JSON. 
  • JSON is stringified() and formatted correctly.
401 Unauthorized When the key provided in the authentication header is not the <dev_key> for this app.
400 Bad request

When the request failed at least one of the validation criteria.

400  Payload is missing or failed to parse
  • Make sure that the JSON is stringified and formatted correctly.
  • If more than one event is included in the JSON payload. Make sure to send one event per request.
500  Internal Server Error Verify that the JSON is stringified() and formatted correctly.
 

Testing

Consider: 

  • For testing, use the AppsFlyer ID of a non-organic install so that test event attributes in realtime. Organic events attribute with a delay of a few hours.
  • Some event fields are populated using the install attribution event of the user. For example, install time and media source. 

To attribute the test device as a non-organic install:

  1. Prepare a custom attribution link for testing S2S messages. Set the media source parameter on the link as to s2s_test. Example: &pid=s2s_test 
  2. Register the test device
  3. Uninstall the app from the test device.
  4. Send the link to the test device.
  5. Click on the link.
  6. Install and then launch the app.
  7. In the dashboard, check to see that the install is attributed.
  8. Fetch the AppsFlyer ID to use in your S2S message.

To send the S2S test message:

  1. Prepare an S2S message using the AppsFlyer ID allocated. See the following for code examples.
  2. Send the message.
  3. Do one of the following to see how the message is recorded in AppsFlyer:
  4. Check and view that:
    • Values sent in the S2S message correctly populate the report. Pay special attention to the Event date, Event currency, Event revenue, and Event value fields.
    • The attribution source and install time are populated by AppsFlyer.
    • Values provided by the SDK itself like SDK version are not populated.

Example code for sending S2S messages

JavaPythonNode JSC#PHPGo
/* using the okhttp package 
install from maven https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp */
import okhttp3.*;
import java.io.IOException;

public class SendRequest {
  public static void main(String[] args) {

    OkHttpClient client = new OkHttpClient();
    MediaType mediaType = MediaType.parse("application/json");

    RequestBody body = RequestBody.create(mediaType, "" +
        "{\r\n\t\"appsflyer_id\": \"<APPS_FLYER_ID>\"," +
        "\r\n\t\"customer_user_id\": \"123456\",\r\n\t\"eventName\": \"af_purchase\",\r\n\t\"" +
        "eventValue\": \"{\\\"af_revenue\\\":\\\"6\\\" ,\\\"af_content_id\\\":\\\"15854\\\"}\",\r\n\t\"" +
        "eventCurrency\": \"USD\",\r\n\t\"" +
        "eventTime\": \"2018-08-10 4:17:00.000\",\r\n\t\"");

    Request request = new Request.Builder()
        .url("https://api2.appsflyer.com/inappevent/<APP_ID>")
        .post(body)
        .addHeader("Content-Type", "application/json")
        .addHeader("authentication", "<YOUR_DEV_KEY>")
        .build();

    try {
      Response response = client.newCall(request).execute();
      System.out.println(response.code());
      System.out.println(response.body().string());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Sending the advertising ID/device identifier is important

  • The advertising ID/device identifier is mandatory to assure postbacks to SRNs like Facebook and Google Ads. If you are unable to send the ID, take into consideration that postbacks can't be sent.
  • If you send only the AppsFlyer ID, in-app events will be recorded and attributed correctly.

Populating parameters

Difference between organic and non-organic

When AppsFlyer processes S2S in-app events, attribution fields are populated by using the AppsFlyer ID to identify the associated install event that preceded the in-app events.

What this means is that some data that AppsFlyer associates with non-organic S2S in-app events, is not associated with organic S2S in-app events.

 Example

For example, if you compare raw data reports of non-organic and organic S2S in-app events, non-organic events contain data that organic in-app events do not.

Non-organic in-app events include data about the media source, campaign, attributed touch type, and attributed touch time.

Organic in-app events, on the other hand, follow an organic install. Organic installs don't have data related to campaign, media source, attributed touch type, and install time.

Mapping AppsFlyer ID with customer user ID (CUID)

Backend logic is required to obtain values to populate parameter. The following describes how you can get the AppsFlyer ID:

  • The AppsFlyer ID is mandatory and is used to attribute events.
  • It is generated when a user first installs the mobile app.
  • So that you can map your CUID to the AppsFlyer ID, you need to set the CUID in the app. 

To make it easier for you to know which user performed which event, implement the flow that follows:

  • Set customer user ID when the user installs the app.
  • AppsFlyer raw data reports contain the CUID and AppsFlyer ID. Use one of the data delivery tools to get this or AppsFlyer Push API. 
  • Use the raw data reports to match the CUID with the AppsFlyer ID. 
  • The AppsFlyer ID is available in the SDK integrated into your app (Android/iOS).
  • Map AppsFlyer ID to customer user ID in your internal systems (important for future use).

Once you map AppsFlyer ID with your CUID, you can match the user to events performed. You can then obtain the other values (event value, event currency, event time, etc.) and send the server to server in-app event. 

 Request body example

{
  "appsflyer_id": "1415211453000-6513894",
	"advertising_id": "38412345-8cf0-aa78-b23e-10b96e40000d",
	"eventName": "af_purchase",
	"eventValue": 
	"{
		\"af_revenue\": \"6\",
		\"af_content_type\": \"wallets\",
		\"af_content_id\": \"15854\",
		\"af_quantity\" :\"1\"
   }",
	"eventCurrency": "USD",
	"ip": "1.2.3.4",
	"eventTime": "2014-05-15 12:17:00.000"
}


In this case, AppsFlyer receives an S2S in-app event which represents a purchase event with revenue, as well as additional properties such as content type, etc.

Fetching the AppsFlyer ID

appsflyer_id is a mandatory parameter in server-to-server event messages. AppsFlyer uses this parameter to attribute the event to the original device and attributed media source. You can get the ID using one of the following methods:

 Tip

When testing S2S messages, if you're using raw data, look for the record with media source "s2s_test". This is your test device and its AppsFlyer Device ID is the ID you need.

Sending negative revenue

Events having a negative revenue value can be sent. For example, if a purchase was canceled. The parameter af_revenue can have negative values to record this. 

If you populate af_quantity, you might want to populate it with a negative value depending on your system logic. AppsFlyer doesn't make use of af_quantity.

Example with negative revenue

{
	"eventName": "cancel_purchase",
	"eventValue": 
	"{
		\"af_revenue\": \"-6\",
		\"af_content_type\": \"wallets\",
		\"af_content_id\": \"15854\",
		\"af_quantity\" :\"1\"
   }",
	"eventCurrency": "USD",
	
}

Sending events without event values

If you want to send events without event value, simply pass an empty string to event value: "eventValue":""

AppsFlyer is then able, according to the advertiser's configuration, to send such rich in-app events to media sources for advanced targeting, optimization and audience creation purposes.

Troubleshooting

Events aren't displaying in the dashboard

  • Endpoint: Verify that the endpoint used is correct.
  • Verify that the payload contains the mandatory parameters. See here.
  • Ensure that the AppsFlyer ID you are using to fire the event is a real appsflyer_id and is actually installed on the specific app. Not a test ID provided in the documentation. See here.
  • S2S events don't support multi-events in one S2S request. Each event must be sent as a separate event. 
  • In the overview dashboard, the date range relates to the app install date (LTV) and not to the event date.
    • Make sure you select the correct date range.
    • Make sure that the dashboard date range corresponds to the install date of the device (appsflyer_id) and not the date of the event.
  • Event raw data reports: the date range relates to the event date and not the install date. 

Events don't contain revenue

If you send S2S events but their revenue is not recorded: ensure that the JSON that you send is stringified. The most important part is the event value parameter in the JSON. It must be stringified as shown in the example that follows.

"{\"af_revenue\":\"6\" ,\"af_content_type\":\"wallets\"}"

If it is not stringified then the event value is not processed correctly and revenue is not recorded.

Revenue values must not be formatted in any way. They can contain a decimal comma but this is not obligatory. Don't include currency signs or codes nor , delimiters. The revenue can be prefixed by a -

  • Example valid values are: 123, -123.45123.456 
  • Example Illegal values: 1,234.561,234

Not all fields are populated in S2S events

Raw data fields are populated using the value sent in the S2S call and some fields are populated using the install event.  Similar behavior but not identical is observed for in-app events reported using the AppsFlyer SDK. There are some differences, specifically, the following fields are not populated for S2S events:

  • WIFI
  • Operator
  • Language
  • Device Type
  • Device Category
  • App Version: You can use app_version_name
  • App Name
Was this article helpful?